From 56a4e0fac92fa7996276fc8b9ee92290a314045e Mon Sep 17 00:00:00 2001 From: Cyberes Date: Thu, 27 Jun 2024 22:35:58 -0600 Subject: [PATCH] add sort of working esp32 code --- esp32/README.md | 4 + esp32/telelogger/.gitignore | 5 + esp32/telelogger/Kconfig.projbuild | 473 +++++++ esp32/telelogger/README.md | 49 + esp32/telelogger/config.h | 201 +++ esp32/telelogger/config.xml | 34 + esp32/telelogger/dashboard/cross.png | Bin 0 -> 1526 bytes esp32/telelogger/dashboard/dashboard.js | 126 ++ esp32/telelogger/dashboard/index.html | 33 + esp32/telelogger/dashboard/tick.png | Bin 0 -> 1545 bytes esp32/telelogger/dataserver.cpp | 353 ++++++ esp32/telelogger/platformio.ini | 34 + esp32/telelogger/teleclient.cpp | 737 +++++++++++ esp32/telelogger/teleclient.h | 114 ++ esp32/telelogger/telecrypt.cpp | 120 ++ esp32/telelogger/telecrypt.h | 9 + esp32/telelogger/telelogger.bin | Bin 0 -> 380176 bytes esp32/telelogger/telelogger.ino | 1508 +++++++++++++++++++++++ esp32/telelogger/telemesh.cpp | 113 ++ esp32/telelogger/telemesh.h | 43 + esp32/telelogger/telestore.cpp | 263 ++++ esp32/telelogger/telestore.h | 94 ++ 22 files changed, 4313 insertions(+) create mode 100644 esp32/README.md create mode 100644 esp32/telelogger/.gitignore create mode 100644 esp32/telelogger/Kconfig.projbuild create mode 100644 esp32/telelogger/README.md create mode 100644 esp32/telelogger/config.h create mode 100644 esp32/telelogger/config.xml create mode 100644 esp32/telelogger/dashboard/cross.png create mode 100644 esp32/telelogger/dashboard/dashboard.js create mode 100644 esp32/telelogger/dashboard/index.html create mode 100644 esp32/telelogger/dashboard/tick.png create mode 100644 esp32/telelogger/dataserver.cpp create mode 100644 esp32/telelogger/platformio.ini create mode 100644 esp32/telelogger/teleclient.cpp create mode 100644 esp32/telelogger/teleclient.h create mode 100644 esp32/telelogger/telecrypt.cpp create mode 100644 esp32/telelogger/telecrypt.h create mode 100644 esp32/telelogger/telelogger.bin create mode 100644 esp32/telelogger/telelogger.ino create mode 100644 esp32/telelogger/telemesh.cpp create mode 100644 esp32/telelogger/telemesh.h create mode 100644 esp32/telelogger/telestore.cpp create mode 100644 esp32/telelogger/telestore.h diff --git a/esp32/README.md b/esp32/README.md new file mode 100644 index 0000000..23877e8 --- /dev/null +++ b/esp32/README.md @@ -0,0 +1,4 @@ +```shell +git clone https://github.com/rweather/arduinolibs +cp -r arduinolibs/libraries/Crypto Freematics/firmware_v5/telelogger/lib +``` \ No newline at end of file diff --git a/esp32/telelogger/.gitignore b/esp32/telelogger/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/esp32/telelogger/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/esp32/telelogger/Kconfig.projbuild b/esp32/telelogger/Kconfig.projbuild new file mode 100644 index 0000000..eaa6c25 --- /dev/null +++ b/esp32/telelogger/Kconfig.projbuild @@ -0,0 +1,473 @@ +menu "Telelogger Configuration" + + config ENABLE_OBD + bool "Enable OBD Connection" + default "y" + + config ENABLE_MEMS + bool "Enable MEMS Motion Sensor" + default "y" + + choice STORAGE + bool "Storage Option" + default STORAGE_SD + config STORAGE_NONE + bool "No Storage" + config STORAGE_SPIFFS + bool "SPIFFS" + config STORAGE_SD + bool "MicroSD" + endchoice + + config STORAGE + int + default 0 if STORAGE_NONE + default 1 if STORAGE_SPIFFS + default 2 if STORAGE_SD + + choice GNSS + bool "GNSS Option" + default GNSS_INTERNAL + config GNSS_NONE + bool "No GNSS" + config GNSS_INTERNAL + bool "Internal GNSS" + config GNSS_EXTERNAL + bool "External GNSS" + config GNSS_CELLULAR + bool "Cellular GNSS" + + endchoice + + config GNSS + int + default 0 if GNSS_NONE + default 1 if GNSS_INTERNAL + default 2 if GNSS_EXTERNAL + default 3 if GNSS_CELLULAR + + config BOARD_HAS_PSRAM + bool "Enable PSRAM" + default "n" + help + Enabling PSRAM and use it for data buffer + + config ENABLE_BLE + bool "Enable BLE" + default "n" + help + Enabling BLE GATT server working with Freematics Controller app + + config ENABLE_WIFI + bool "Enable Wi-Fi" + default "n" + help + Enabling seamless Wi-Fi and cellular network co-working + + config WIFI_SSID + string "WiFi Hotspot SSID" + default "" + + config WIFI_PASSWORD + string "WiFi Hotspot Password" + default "" + + config ENABLE_HTTPD + bool "Enable Wi-Fi AP and HTTPd" + default "n" + help + Enabling Wi-Fi AP and HTTPd + + config CELL_APN + string "Cellular Network APN" + default "" + help + If left blank, APN is obtained automatically if supported + + choice SERVER_PROTOCOL + bool "Server Transport Protocol" + default PROTOCOL_UDP + config PROTOCOL_UDP + bool "UDP Protocol" + config PROTOCOL_HTTP + bool "HTTP Protocol" + config PROTOCOL_HTTPS + bool "HTTPS Protocol" + endchoice + + config SERVER_PROTOCOL + int + default 1 if PROTOCOL_UDP + default 2 if PROTOCOL_HTTP + default 3 if PROTOCOL_HTTPS + + config SERVER_HOST + string "Server Host/IP" + default "hub.freematics.com" + + config SERVER_PORT + int "Server Port (0 for auto)" + range 0 65535 + default 0 + +endmenu + +menu "Arduino Configuration" + + config ENABLE_ARDUINO_DEPENDS + bool + select LWIP_SO_RCVBUF + select ETHERNET + select WIFI_ENABLED + select ESP32_PHY_CALIBRATION_AND_DATA_STORAGE if IDF_TARGET_ESP32 + select MEMMAP_SMP + default "y" + + config AUTOSTART_ARDUINO + bool "Autostart Arduino setup and loop on boot" + default "y" + help + Enabling this option will implement app_main and start Arduino. + All you need to implement in your main.cpp is setup() and loop() + and include Arduino.h + If disabled, you can call initArduino() to run any preparations + required by the framework + + choice ARDUINO_RUNNING_CORE + bool "Core on which Arduino's setup() and loop() are running" + default ARDUINO_RUN_CORE1 + help + Select on which core Arduino's setup() and loop() functions run + + config ARDUINO_RUN_CORE0 + bool "CORE 0" + config ARDUINO_RUN_CORE1 + bool "CORE 1" + config ARDUINO_RUN_NO_AFFINITY + bool "BOTH" + + endchoice + + config ARDUINO_RUNNING_CORE + int + default 0 if ARDUINO_RUN_CORE0 + default 1 if ARDUINO_RUN_CORE1 + default -1 if ARDUINO_RUN_NO_AFFINITY + + config ARDUINO_LOOP_STACK_SIZE + int "Loop thread stack size" + default 8192 + help + Amount of stack available for the Arduino task. + + choice ARDUINO_EVENT_RUNNING_CORE + bool "Core on which Arduino's event handler is running" + default ARDUINO_EVENT_RUN_CORE1 + help + Select on which core Arduino's WiFi.onEvent() run + + config ARDUINO_EVENT_RUN_CORE0 + bool "CORE 0" + config ARDUINO_EVENT_RUN_CORE1 + bool "CORE 1" + config ARDUINO_EVENT_RUN_NO_AFFINITY + bool "BOTH" + + endchoice + + config ARDUINO_EVENT_RUNNING_CORE + int + default 0 if ARDUINO_EVENT_RUN_CORE0 + default 1 if ARDUINO_EVENT_RUN_CORE1 + default -1 if ARDUINO_EVENT_RUN_NO_AFFINITY + + choice ARDUINO_UDP_RUNNING_CORE + bool "Core on which Arduino's UDP is running" + default ARDUINO_UDP_RUN_CORE1 + help + Select on which core Arduino's UDP run + + config ARDUINO_UDP_RUN_CORE0 + bool "CORE 0" + config ARDUINO_UDP_RUN_CORE1 + bool "CORE 1" + config ARDUINO_UDP_RUN_NO_AFFINITY + bool "BOTH" + + endchoice + + config ARDUINO_UDP_TASK_PRIORITY + int "Priority of the UDP task" + default 3 + help + Select at what priority you want the UDP task to run. + + config ARDUINO_UDP_RUNNING_CORE + int + default 0 if ARDUINO_UDP_RUN_CORE0 + default 1 if ARDUINO_UDP_RUN_CORE1 + default -1 if ARDUINO_UDP_RUN_NO_AFFINITY + + config ARDUINO_ISR_IRAM + bool "Run interrupts in IRAM" + default "n" + help + Enabling this option will Attach all interrupts with the IRAm flag. + It will also make some HAL function, like, digitalRead/Write and more + be loaded into IRAM for access inside ISRs. + Beware that this is a very dangerous setting. Enable it only if you + are fully aware of the consequences. + + config DISABLE_HAL_LOCKS + bool "Disable mutex locks for HAL" + default "n" + help + Enabling this option will run all hardware abstraction without locks. + While communication with external hardware will be faster, you need to + make sure that there is no option to use the same bus from another thread + or interrupt at the same time. Option is best used with Arduino enabled + and code implemented only in setup/loop and Arduino callbacks + + menu "Debug Log Configuration" + choice ARDUHAL_LOG_DEFAULT_LEVEL + bool "Default log level" + default ARDUHAL_LOG_DEFAULT_LEVEL_ERROR + help + Specify how much output to see in logs by default. + + config ARDUHAL_LOG_DEFAULT_LEVEL_NONE + bool "No output" + config ARDUHAL_LOG_DEFAULT_LEVEL_ERROR + bool "Error" + config ARDUHAL_LOG_DEFAULT_LEVEL_WARN + bool "Warning" + config ARDUHAL_LOG_DEFAULT_LEVEL_INFO + bool "Info" + config ARDUHAL_LOG_DEFAULT_LEVEL_DEBUG + bool "Debug" + config ARDUHAL_LOG_DEFAULT_LEVEL_VERBOSE + bool "Verbose" + endchoice + + config ARDUHAL_LOG_DEFAULT_LEVEL + int + default 0 if ARDUHAL_LOG_DEFAULT_LEVEL_NONE + default 1 if ARDUHAL_LOG_DEFAULT_LEVEL_ERROR + default 2 if ARDUHAL_LOG_DEFAULT_LEVEL_WARN + default 3 if ARDUHAL_LOG_DEFAULT_LEVEL_INFO + default 4 if ARDUHAL_LOG_DEFAULT_LEVEL_DEBUG + default 5 if ARDUHAL_LOG_DEFAULT_LEVEL_VERBOSE + + config ARDUHAL_LOG_COLORS + bool "Use ANSI terminal colors in log output" + default "n" + help + Enable ANSI terminal color codes in bootloader output. + In order to view these, your terminal program must support ANSI color codes. + + config ARDUHAL_ESP_LOG + bool "Forward ESP_LOGx to Arduino log output" + default "n" + help + This option will redefine the ESP_LOGx macros to Arduino's log_x macros. + To enable for your application, add the follwing after your includes: + #ifdef ARDUINO_ARCH_ESP32 + #include "esp32-hal-log.h" + #endif + + endmenu + + choice ARDUHAL_PARTITION_SCHEME + bool "Used partition scheme" + default ARDUHAL_PARTITION_SCHEME_DEFAULT + help + Specify which partition scheme to be used. + + config ARDUHAL_PARTITION_SCHEME_DEFAULT + bool "Default" + config ARDUHAL_PARTITION_SCHEME_MINIMAL + bool "Minimal (for 2MB FLASH)" + config ARDUHAL_PARTITION_SCHEME_NO_OTA + bool "No OTA (for large apps)" + config ARDUHAL_PARTITION_SCHEME_HUGE_APP + bool "Huge App (for very large apps)" + config ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS + bool "Minimal SPIFFS (for large apps with OTA)" + endchoice + + config ARDUHAL_PARTITION_SCHEME + string + default "default" if ARDUHAL_PARTITION_SCHEME_DEFAULT + default "minimal" if ARDUHAL_PARTITION_SCHEME_MINIMAL + default "no_ota" if ARDUHAL_PARTITION_SCHEME_NO_OTA + default "huge_app" if ARDUHAL_PARTITION_SCHEME_HUGE_APP + default "min_spiffs" if ARDUHAL_PARTITION_SCHEME_MIN_SPIFFS + + + config AUTOCONNECT_WIFI + bool "Autoconnect WiFi on boot" + default "n" + depends on AUTOSTART_ARDUINO + select ARDUINO_SELECTIVE_WiFi + help + If enabled, WiFi will connect to the last used SSID (if station was enabled), + else connection will be started only after calling WiFi.begin(ssid, password) + + config ARDUINO_SELECTIVE_COMPILATION + bool "Include only specific Arduino libraries" + default n + + config ARDUINO_SELECTIVE_ArduinoOTA + bool "Enable ArduinoOTA" + depends on ARDUINO_SELECTIVE_COMPILATION + select ARDUINO_SELECTIVE_WiFi + select ARDUINO_SELECTIVE_ESPmDNS + default y + + config ARDUINO_SELECTIVE_AsyncUDP + bool "Enable AsyncUDP" + depends on ARDUINO_SELECTIVE_COMPILATION + default y + + config ARDUINO_SELECTIVE_AzureIoT + bool "Enable AzureIoT" + depends on ARDUINO_SELECTIVE_COMPILATION + select ARDUINO_SELECTIVE_HTTPClient + default y + + config ARDUINO_SELECTIVE_BLE + bool "Enable BLE" + depends on ARDUINO_SELECTIVE_COMPILATION + default y + + config ARDUINO_SELECTIVE_BluetoothSerial + bool "Enable BluetoothSerial" + depends on ARDUINO_SELECTIVE_COMPILATION + default y + + config ARDUINO_SELECTIVE_DNSServer + bool "Enable DNSServer" + depends on ARDUINO_SELECTIVE_COMPILATION + select ARDUINO_SELECTIVE_WiFi + default y + + config ARDUINO_SELECTIVE_EEPROM + bool "Enable EEPROM" + depends on ARDUINO_SELECTIVE_COMPILATION + default y + + config ARDUINO_SELECTIVE_ESP32 + bool "Enable ESP32" + depends on ARDUINO_SELECTIVE_COMPILATION + default y + + config ARDUINO_SELECTIVE_ESPmDNS + bool "Enable ESPmDNS" + depends on ARDUINO_SELECTIVE_COMPILATION + select ARDUINO_SELECTIVE_WiFi + default y + + config ARDUINO_SELECTIVE_FFat + bool "Enable FFat" + depends on ARDUINO_SELECTIVE_COMPILATION + select ARDUINO_SELECTIVE_FS + default y + + config ARDUINO_SELECTIVE_FS + bool "Enable FS" + depends on ARDUINO_SELECTIVE_COMPILATION + default y + + config ARDUINO_SELECTIVE_HTTPClient + bool "Enable HTTPClient" + depends on ARDUINO_SELECTIVE_COMPILATION + select ARDUINO_SELECTIVE_WiFi + select ARDUINO_SELECTIVE_WiFiClientSecure + default y + + config ARDUINO_SELECTIVE_LITTLEFS + bool "Enable LITTLEFS" + depends on ARDUINO_SELECTIVE_COMPILATION + select ARDUINO_SELECTIVE_FS + default y + + config ARDUINO_SELECTIVE_NetBIOS + bool "Enable NetBIOS" + depends on ARDUINO_SELECTIVE_COMPILATION + select ARDUINO_SELECTIVE_WiFi + default y + + config ARDUINO_SELECTIVE_Preferences + bool "Enable Preferences" + depends on ARDUINO_SELECTIVE_COMPILATION + default y + + config ARDUINO_SELECTIVE_SD + bool "Enable SD" + depends on ARDUINO_SELECTIVE_COMPILATION + select ARDUINO_SELECTIVE_FS + default y + + config ARDUINO_SELECTIVE_SD_MMC + bool "Enable SD_MMC" + depends on ARDUINO_SELECTIVE_COMPILATION + select ARDUINO_SELECTIVE_FS + default y + + config ARDUINO_SELECTIVE_SimpleBLE + bool "Enable SimpleBLE" + depends on ARDUINO_SELECTIVE_COMPILATION + default y + + config ARDUINO_SELECTIVE_SPI + bool "Enable SPI" + depends on ARDUINO_SELECTIVE_COMPILATION + default y + + config ARDUINO_SELECTIVE_SPIFFS + bool "Enable SPIFFS" + depends on ARDUINO_SELECTIVE_COMPILATION + select ARDUINO_SELECTIVE_FS + default y + + config ARDUINO_SELECTIVE_Ticker + bool "Enable Ticker" + depends on ARDUINO_SELECTIVE_COMPILATION + default y + + config ARDUINO_SELECTIVE_Update + bool "Enable Update" + depends on ARDUINO_SELECTIVE_COMPILATION + default y + + config ARDUINO_SELECTIVE_WebServer + bool "Enable WebServer" + depends on ARDUINO_SELECTIVE_COMPILATION + default y + select ARDUINO_SELECTIVE_FS + + config ARDUINO_SELECTIVE_WiFi + bool "Enable WiFi" + depends on ARDUINO_SELECTIVE_COMPILATION + default y + + config ARDUINO_SELECTIVE_WiFiClientSecure + bool "Enable WiFiClientSecure" + depends on ARDUINO_SELECTIVE_COMPILATION + select ARDUINO_SELECTIVE_WiFi + default y + + config ARDUINO_SELECTIVE_WiFiProv + bool "Enable WiFiProv" + depends on ARDUINO_SELECTIVE_COMPILATION + select ARDUINO_SELECTIVE_WiFi + default y + + config ARDUINO_SELECTIVE_Wire + bool "Enable Wire" + depends on ARDUINO_SELECTIVE_COMPILATION + default y + + + endmenu + \ No newline at end of file diff --git a/esp32/telelogger/README.md b/esp32/telelogger/README.md new file mode 100644 index 0000000..69aa720 --- /dev/null +++ b/esp32/telelogger/README.md @@ -0,0 +1,49 @@ +This Arduino sketch is developed for [Freematics ONE+](https://freematics.com/products/freematics-one-plus/) to collect vehicle telemetry data from OBD, GPS, motion sensor, to log the data in local storage and to transmit the data to a remote server in real-time. It demonstrates most capabilities of Freematics ONE+ and works well with [Traccar](https://www.traccar.org) GPS tracking platform. + +Data Collection +--------------- + +The sketch collects following data. + +* Vehicle OBD data (from OBD port) +* Battery voltage (from OBD port) +* Geolocation data (from internal or external GNSS) +* Accelerometer and gyroscope data (from internal MEMS motion sensor) +* Cellular or WiFi network signal level +* Device temperature + +Collected data are stored in a circular buffer in ESP32's IRAM or PSRAM. When PSRAM is enabled, hours of data can be buffered in case of temporary network outage and transmitted when network connection resumes. + +Data Transmission +----------------- + +Data transmission over UDP and HTTP(s) protocols are implemented for the followings. + +* WiFi (ESP32 built-in) +* 3G WCDMA (SIM5360) +* 4G LTE CAT-4 (SIM7600) +* 4G LTE CAT-M (SIM7070) + +UDP mode implements a telemetry client for [Freematics Hub](https://hub.freematics.com) and [Traccar](https://www.traccar.org). HTTP(s) mode implements [OsmAnd](https://www.traccar.org/osmand/) protocol with additional data sent as POST payload. + +Seamless WiFi and cellular network co-working is implemented. When defined WiFi hotspot is available, data is transmitted via WiFi and cellular module is switched off. When no WiFi hotspot can be reached, cellular module is switched on for data transmission until WiFi hotspot available again. + +Data Storage +------------ + +Following types of data storage are supported. + +* MicroSD card storage +* ESP32 built-in Flash memory storage (SPIFFS) + +BLE & App +--------- + +A BLE SPP server is implemented in [FreematicsPlus](https://github.com/stanleyhuangyc/Freematics/blob/master/libraries/FreematicsPlus) library. To enable BLE support, change ENABLE_BLE to 1 [config.h](config.h). This will enable remote control and data monitoring via [Freematics Controller App](https://freematics.com/software/freematics-controller/). + +Prerequisites +------------- + +* Freematics ONE+ [Model A](https://freematics.com/products/freematics-one-plus/), [Model B](https://freematics.com/products/freematics-one-plus-model-b/), [Model H](https://freematics.com/products/freematics-one-plus-model-h/) +* A micro SIM card if cellular network connectivity required +* [PlatformIO](http://platformio.org/), [Arduino IDE](https://github.com/espressif/arduino-esp32#installation-instructions), [Freematics Builder](https://freematics.com/software/arduino-builder) or [ESP-IDF](https://github.com/espressif/esp-idf) for compiling and uploading code diff --git a/esp32/telelogger/config.h b/esp32/telelogger/config.h new file mode 100644 index 0000000..3d3da3c --- /dev/null +++ b/esp32/telelogger/config.h @@ -0,0 +1,201 @@ +#ifndef CONFIG_H_INCLUDED +#define CONFIG_H_INCLUDED + +#ifdef CONFIG_ENABLE_OBD +#define ENABLE_OBD CONFIG_ENABLE_OBD +#endif +#ifdef CONFIG_ENABLE_MEMS +#define ENABLE_MEMS CONFIG_ENABLE_MEMS +#endif +#ifdef CONFIG_GNSS +#define GNSS CONFIG_GNSS +#endif +#ifdef CONFIG_STORAGE +#define STORAGE CONFIG_STORAGE +#endif +#ifdef CONFIG_BOARD_HAS_PSRAM +#define BOARD_HAS_PSRAM 1 +#endif +#ifdef CONFIG_ENABLE_WIFI +#define ENABLE_WIFI CONFIG_ENABLE_WIFI +#define WIFI_SSID CONFIG_WIFI_SSID +#define WIFI_PASSWORD CONFIG_WIFI_PASSWORD +#endif +#ifdef CONFIG_ENABLE_BLE +#define ENABLE_BLE CONFIG_ENABLE_BLE +#endif +#ifdef CONFIG_ENABLE_HTTPD +#define ENABLE_HTTPD CONFIG_ENABLE_HTTPD +#endif +#ifdef CONFIG_SERVER_HOST +#define SERVER_HOST CONFIG_SERVER_HOST +#define SERVER_PORT CONFIG_SERVER_PORT +#define SERVER_PROTOCOL CONFIG_SERVER_PROTOCOL +#endif +#ifdef CONFIG_CELL_APN +#define CELL_APN CONFIG_CELL_APN +#endif + +/************************************** +* Circular Buffer Configuration +**************************************/ +#if BOARD_HAS_PSRAM +#define BUFFER_SLOTS 1024 /* max number of buffer slots */ +#define BUFFER_LENGTH 384 /* bytes per slot */ +#define SERIALIZE_BUFFER_SIZE 4096 /* bytes */ +#else +#define BUFFER_SLOTS 32 /* max number of buffer slots */ +#define BUFFER_LENGTH 256 /* bytes per slot */ +#define SERIALIZE_BUFFER_SIZE 1024 /* bytes */ +#endif + +/************************************** +* Configuration Definitions +**************************************/ +#define STORAGE_NONE 0 +#define STORAGE_SPIFFS 1 +#define STORAGE_SD 2 + +#define GNSS_NONE 0 +#define GNSS_STANDALONE 1 +#define GNSS_CELLULAR 2 + +#define PROTOCOL_UDP 1 +#define PROTOCOL_HTTP 2 +#define PROTOCOL_HTTPS 3 + +#define PROTOCOL_METHOD_GET 0 +#define PROTOCOL_METHOD_POST 1 + +/************************************** +* OBD-II configurations +**************************************/ +#ifndef ENABLE_OBD +#define ENABLE_OBD 1 +#endif + +// maximum consecutive OBD access errors before entering standby +#define MAX_OBD_ERRORS 3 + +/************************************** +* Networking configurations +**************************************/ +#ifndef ENABLE_WIFI +#define ENABLE_WIFI 1 +// WiFi settings +#define WIFI_SSID "example" +#define WIFI_PASSWORD "1234" +#endif + +#ifndef SERVER_HOST +// cellular network settings +#define CELL_APN "hologram" +// Freematics Hub server settings +#define SERVER_HOST "192.168.1.114" +#define SERVER_PROTOCOL PROTOCOL_UDP +#endif + +#define SERVER_ENCRYPTION_ENABLE 1 +#define CHACHA20_KEY "d38a3b96a26d0b1139bd30c174884f5dbc8eaaf492493725633ecebfa4ab19e9" + +#ifndef CONFIG_MBEDTLS_CHACHAPOLY_C +#define CONFIG_MBEDTLS_CHACHAPOLY_C y +#endif + +// SIM card setting +#define SIM_CARD_PIN "" + +// HTTPS settings +#define SERVER_METHOD PROTOCOL_METHOD_POST +#define SERVER_PATH "/hub/api" + +#if !SERVER_PORT +#undef SERVER_PORT +#if SERVER_PROTOCOL == PROTOCOL_UDP +#define SERVER_PORT 5171 +#elif SERVER_PROTOCOL == PROTOCOL_HTTP +#define SERVER_PORT 80 +#elif SERVER_PROTOCOL == PROTOCOL_HTTPS +#define SERVER_PORT 443 +#endif +#endif + +// WiFi Mesh settings +#define WIFI_MESH_ID "123456" +#define WIFI_MESH_CHANNEL 13 + +// WiFi AP settings +#define WIFI_AP_SSID "TELELOGGER" +#define WIFI_AP_PASSWORD "PASSWORD" + +// maximum consecutive communication errors before resetting network +#define MAX_CONN_ERRORS_RECONNECT 5 +// maximum allowed connecting time +#define MAX_CONN_TIME 10000 /* ms */ +// data receiving timeout +#define DATA_RECEIVING_TIMEOUT 5000 /* ms */ +// expected maximum server sync signal interval +#define SERVER_SYNC_INTERVAL 120 /* seconds, 0 to disable */ +// data interval settings +#define STATIONARY_TIME_TABLE {10, 60, 180} /* seconds */ +#define DATA_INTERVAL_TABLE {1000, 2000, 5000} /* ms */ +#define PING_BACK_INTERVAL 900 /* seconds */ +#define SIGNAL_CHECK_INTERVAL 10 /* seconds */ + +/************************************** +* Data storage configurations +**************************************/ +#ifndef STORAGE +// change the following line to change storage type +#define STORAGE STORAGE_NONE +#endif + +/************************************** +* MEMS sensors +**************************************/ +#ifndef ENABLE_MEMS +#define ENABLE_MEMS 1 +#endif + +/************************************** +* GPS +**************************************/ +#ifndef GNSS +// change the following line to change GNSS setting +#define GNSS GNSS_STANDALONE +#endif +// keeping GNSS power on during standby +#define GNSS_ALWAYS_ON 0 +// GNSS reset timeout while no signal +#define GNSS_RESET_TIMEOUT 300 /* seconds */ + +/************************************** +* Standby/wakeup +**************************************/ +// motion threshold for waking up +#define MOTION_THRESHOLD 0.4f /* vehicle motion threshold in G */ +// engine jumpstart voltage for waking up (when MEMS unavailable) +#define JUMPSTART_VOLTAGE 14 /* V */ +// reset device after waking up +#define RESET_AFTER_WAKEUP 1 + +/************************************** +* Additional features +**************************************/ +#define PIN_SENSOR1 34 +#define PIN_SENSOR2 26 + +#define COOLING_DOWN_TEMP 75 /* celsius degrees */ + +// enable(1)/disable(0) http server +#ifndef ENABLE_HTTPD +#define ENABLE_HTTPD 0 +#endif + +// enable(1)/disable(0) BLE SPP server (for Freematics Controller App). +#ifndef ENABLE_BLE +#define ENABLE_BLE 0 +#endif + + +#endif // CONFIG_H_INCLUDED diff --git a/esp32/telelogger/config.xml b/esp32/telelogger/config.xml new file mode 100644 index 0000000..900c2f7 --- /dev/null +++ b/esp32/telelogger/config.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/esp32/telelogger/dashboard/cross.png b/esp32/telelogger/dashboard/cross.png new file mode 100644 index 0000000000000000000000000000000000000000..b3829c9c5c37fa025380443ddc1606e37f0a35d8 GIT binary patch literal 1526 zcmd^<`CHNl0LIB$hkivA^U174thHqxEDuCbL68U$#A~q9JkSo)GM1T6%(9|qhnqDk z%MPtH6*(kKt!BrjYqi!&P|h!2p{a3biq~xWGxk2u`+lD1{qg|av=EhoKy~iav&v>!{nr-=O60gU?GsD z5JprSsSa){2JGh-7OWGNJ_f_YVl~@CjVzWT6g$9VPQDvyhC-jLU$2df)J8X{*v`&6IbmY6K{8nov{geSYDpyFDqE?8gC-=TKPE;L?5k%mR2YmlAW-7wWa$I7 zK|vw_XmfX!z@T-T+$3wA+LPkv=I7LTTsa1#3JO#t#EnJM`FD28WNzUxR1Q2t!akS$n}-)hcvQ3(UznJJTDE(}afhVR3JKd}k&n2F{n?1pqA? z{pj$1MO}?CA+c+t=Yu`TT}Wg<9$)VaG{Nof!eFX>nd6KYH5O|%o0Nh(Dq+183LOv& zC&xxL=wNwGl_1bdPp3aXtR1HA(8|P$+t(#qJsaRaqZ6PC2xv=+8>Udy1cC&&O$u{p z+`IuibLvTc?f{YaThWoB%a;t>DJo%u#10U;I)HS#DlPSO)ddra-Ff_2=ZQiciDaGn zpfCGQPbRDJ;hGX2NFWH%-a204y$vqE>`Up$&UhKmkOu^)B~1ouR99)y(*iC?qm5Fj zN))Qj%R?6yW-$!C@$$OA##Ll%r{JXaWTaUJ`<3Y_y3!L9hPPAi$7iOe9v{h8rXcGd)=<<(a#Y_`Dvb45^KADynLD8G+fr{kXjBO{f(f{`86 zTE|sdnPh|()gQxX3i57n0-kUF%&1W{!U2`P|5$Xi#^1j!DSmi6MNwO=A`n{L*NAc3 zz=FJHM+d2++RQ(Kg5k?krX|o?5u#;tm2iE?6$9y7Yd^eUcL70 z(Ts20(1~R-*=eE6Tz6I6%MLj!=iz8&~4>pE$nbh7VwJjs66+RsPw4B zDefhM_ThY2efQkVhjG!BA~5e%%awbtDqD(of;YoUx|F=Ju_hq2t>%+;-#5&!SRVCU zUl@yQLe-itpDQaDF>Ls&=m#@F`vf1(L4Cz|8i<`=vaxg4;%JxCTTux-Y&lTHe_<-w zBX8Y;I8^Xs@MAzaK(%~ZY~z+&eA39yf2@!I7u`!IYl`2sh;zfras?}C!#0@Ei^gxz z@V`mHFU+!e&fyjftGx>3p_W#{{^aky@x^kue5dZH`+M;+X9SMJ`pMCV342nXS!t4- zC*w;5{OfmV%%`u+zI|t@e7BV2g&D(bVN6vPlLu{=mY$yF{m{On2Sa8(FEH%(2`^p( YsfQahXI#=Ki$@NjkfMnK!tRoP04IiV?EnA( literal 0 HcmV?d00001 diff --git a/esp32/telelogger/dashboard/dashboard.js b/esp32/telelogger/dashboard/dashboard.js new file mode 100644 index 0000000..4c39f96 --- /dev/null +++ b/esp32/telelogger/dashboard/dashboard.js @@ -0,0 +1,126 @@ +var source; +var origin; + +window.addEventListener("message", receiveMessage, false); + +function receiveMessage(event) { + //event.source.postMessage(event.data,event.origin); + source = event.source; + origin = event.origin; + processInput(event.data); +} + +function sendMessage(data) { + if (source) source.postMessage(data, origin); +} + +function checkData(data, key) +{ + var i; + if ((i = data.lastIndexOf(key)) >= 0) { + var j = data.indexOf("\r", i); + return j >= 0 ? data.substr(i + key.length, j - i - key.length) : data.substr(i + key.length); + } + return null; +} + +var imgTick = ""; +var imgCross = ""; + +var con = ""; +var inited = false; + +function processInput(data) +{ + var i; + var ret; + if (con.length > 1024) con = con.substr(512); + con += data; + if (!inited) { + if (ret = checkData(con, "FLASH:")) { + document.getElementById("flash_size").innerText = ret; + } + if (ret = checkData(con, "PSRAM:")) { + document.getElementById("psram_size").innerText = ret.substr(0, 2) == "E " ? "N/A" : ret; + } + if (ret = checkData(con, "TYPE:")) { + var type = parseInt(ret); + var typeName = "Unknown"; + if (type == 11 || type == 16) { + typeName = "Freematics ONE+ Model A"; + } else if (type >= 12 && type <= 14) { + typeName = "Freematics ONE+ Model B"; + } else if (type == 15) { + typeName = "Freematics ONE+ Model H"; + } + document.getElementById("devtype").innerText = typeName; + } + if (ret = checkData(con, "DEVICE ID:")) { + document.getElementById("devid").value = ret; + } + if (ret = checkData(con, "RTC:")) { + document.getElementById("rtc").innerText = ret; + } + if (ret = checkData(con, "SD:")) { + document.getElementById("sd_size").innerHTML = ret; + } + if (ret = checkData(con, "NO SD CARD") != null) { + document.getElementById("sd_size").innerHTML = "NO CARD"; + } + if (ret = checkData(con, "GNSS:")) { + document.getElementById("gps").innerHTML = ret.indexOf("NO") >= 0 ? imgCross : imgTick; + } + if (ret = checkData(con, "OBD:")) { + document.getElementById("obd").innerHTML = ret.indexOf("NO") >= 0 ? imgCross : imgTick; + } + if (ret = checkData(con, "MEMS:")) { + document.getElementById("mems").innerHTML = ret.indexOf("NO") >= 0 ? imgCross : (imgTick + " " + ret); + } + if (ret = checkData(con, "HTTPD:")) { + document.getElementById("wifi").innerHTML = ret.indexOf("NO") >= 0 ? imgCross : imgTick; + } + if (ret = checkData(con, "WiFi IP:")) { + document.getElementById("wifi").innerHTML = imgTick + " IP:" + ret; + } + if (ret = checkData(con, "IMEI:")) { + document.getElementById("imei").innerText = "IMEI:" + ret; + } + if (ret = checkData(con, "CELL:")) { + document.getElementById("cellinfo").innerHTML = ret == "NO" ? imgCross : (imgTick + " " + ret); + } + if ((ret = checkData(con, "NO SIM CARD")) != null) { + document.getElementById("imei").innerHTML = "| NO SIM CARD"; + } + if (ret = checkData(con, "Operator:")) { + document.getElementById("imei").innerText = "| " + ret; + } + if (ret = checkData(con, "Unable to connect") != null) { + document.getElementById("server").innerHTML = imgCross; + } + if (ret = checkData(con, "LOGIN")) { + document.getElementById("server").innerText = "Connecting to server" + ret; + } + } + if (ret = checkData(con, "[NET]")) { + document.getElementById("server").innerText = ret; + inited = true; + } + if (ret = checkData(con, "[BUF]")) { + document.getElementById("buffer").innerText = ret; + } + if (ret = checkData(con, "[FILE]")) { + document.getElementById("file").innerText = ret; + } + if (ret = checkData(con, "[GPS]")) { + document.getElementById("gps").innerText = ret; + } + if (ret = checkData(con, "[WIFI]")) { + document.getElementById("wifi").innerText = ret; + } + if (ret = checkData(con, "[CELL]")) { + document.getElementById("cell").innerText = ret; + } + if (ret = checkData(con, "RSSI:")) { + document.getElementById("rssi").innerText = "| RSSI:" + ret; + } +} diff --git a/esp32/telelogger/dashboard/index.html b/esp32/telelogger/dashboard/index.html new file mode 100644 index 0000000..89248b6 --- /dev/null +++ b/esp32/telelogger/dashboard/index.html @@ -0,0 +1,33 @@ + + + + + + + +
    +
  • Device ID:
  • +
  • Device Type:
  • +
  • FLASH:
  • +
  • PSRAM:
  • +
  • SD Card:
  • +
  • Motion Sensor:
  • +
  • GNSS:
  • +
  • OBD:
  • +
  • Cellular Module:
  • +
  • Cellular:
  • +
  • Wi-Fi:
  • +
  • Buffer:
  • +
  • Server:
  • +
  • File:
  • +
+ + + diff --git a/esp32/telelogger/dashboard/tick.png b/esp32/telelogger/dashboard/tick.png new file mode 100644 index 0000000000000000000000000000000000000000..b7eed914fdf2edf90e7d88786f84f846b949685a GIT binary patch literal 1545 zcmd^9{Xf$Q9AD?UN?Y24WV3x|&)5S!kd&!7NvWe(u2SK!hG^>>mdB=a9lA?-Xx5hJ zBFr!&QW)nch2`O1h-;`$L<^VNt-s^muh;wO*U#(odS5@{;R-WE8$uuunA?75PrakQ zajk(~>%3>!dNT!Geg?h5gTP2XejsE|K)8P(-tC+pC(tv{FF?R+58MTT7(k93JnDij zAlil7!33soKTNK;!ZvdYk_Rkk=Yc$0byvAX2a^(Lu~CRdfxr>5r*Rap0YqE%TFY8W zEW22>iVh?nK!)SYA5%#+R!-M7!hmzII-QQzq7OI|5zwT1Y+ge{i^#|<98qT3);b_=5LtAx@~d*X zI)~+o3d{Q}as_QuKK^vMZzL#alr_Nx*oXNQg<=_@%)&=_0+U9pRd%ReG&ME$l4|I* zET(tL@#*O~Vl=(*UP*jT6t0ZQ7_=Gb*En*WN*an}rOJqhVK1JKQd=2#32kXCmuJ+2fz$4Iw1mofpR|O z94(d^o*c?A;iIl%$*oo>E@oN#^`z2gRQ-y{XSk;A!(22!By~NJ;LVb6w@zZ*x}7f0 zl&0KH9eh4m)go7R%kdJDthwx`Jg=>BcJ}9Y?ugoM!`Whc&QADLNDpALSp@+-J-qbw{|t!N;u^C47Ps7-_Z*EH_FppkjjP1IPbscpk#-)<*!BSaPTnc5IVg#rui|)*1=%P@RxO_ z7hz>cIK#7IuVmJwL-1$8C^fa z_KJ8&^vXD-l`Ey( z+nUvX{?3{=e7a`A7?H47hhiT}`hJ8sxwE!`?{qW1?k_a8)ycsA$_KCN+}zwW!a|+H s+#kB0)8{Y2>=rL&I#~KLof1|-9 +* Distributed under BSD license +* Visit https://freematics.com/products/freematics-one-plus for more info +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +* +* Implemented HTTP APIs: +* /api/info - device info +* /api/live - live data (OBD/GPS/MEMS) +* /api/control - issue a control command +* /api/list - list of log files +* /api/log/ - raw CSV format log file +* /api/delete/ - delete file +* /api/data/?pid= - JSON array of PID data +*************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "config.h" + +#if ENABLE_HTTPD + +#define WIFI_TIMEOUT 5000 + +extern uint32_t fileid; + +extern "C" +{ +uint8_t temprature_sens_read(); +uint32_t hall_sens_read(); +} + +HttpParam httpParam; + +int handlerLiveData(UrlHandlerParam* param); +int handlerControl(UrlHandlerParam* param); + +uint16_t hex2uint16(const char *p); + +int handlerInfo(UrlHandlerParam* param) +{ + char *buf = param->pucBuffer; + int bufsize = param->bufSize; + int bytes = snprintf(buf, bufsize, "{\"httpd\":{\"uptime\":%u,\"clients\":%u,\"requests\":%u,\"traffic\":%u},\n", + (unsigned int)millis(), httpParam.stats.clientCount, (unsigned int)httpParam.stats.reqCount, (unsigned int)(httpParam.stats.totalSentBytes >> 10)); + + time_t now; + time(&now); + struct tm timeinfo = { 0 }; + localtime_r(&now, &timeinfo); + if (timeinfo.tm_year) { + bytes += snprintf(buf + bytes, bufsize - bytes, "\"rtc\":{\"date\":\"%04u-%02u-%02u\",\"time\":\"%02u:%02u:%02u\"},\n", + timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday, + timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec); + } + + int deviceTemp = (int)temprature_sens_read() * 165 / 255 - 40; + bytes += snprintf(buf + bytes, bufsize - bytes, "\"cpu\":{\"temperature\":%d,\"magnetic\":%d},\n", + deviceTemp, hall_sens_read()); + +#if STORAGE == STORAGE_SPIFFS + bytes += snprintf(buf + bytes, bufsize - bytes, "\"spiffs\":{\"total\":%u,\"used\":%u}", + SPIFFS.totalBytes(), SPIFFS.usedBytes()); +#else + bytes += snprintf(buf + bytes, bufsize - bytes, "\"sd\":{\"total\":%llu,\"used\":%llu}", + SD.totalBytes(), SD.usedBytes()); +#endif + + if (bytes < bufsize - 1) buf[bytes++] = '}'; + + param->contentLength = bytes; + param->contentType=HTTPFILETYPE_JSON; + return FLAG_DATA_RAW; +} + +class LogDataContext { +public: + File file; + uint32_t tsStart; + uint32_t tsEnd; + uint16_t pid; +}; + +int handlerLogFile(UrlHandlerParam* param) +{ + LogDataContext* ctx = (LogDataContext*)param->hs->ptr; + param->contentType = HTTPFILETYPE_TEXT; + if (ctx) { + if (!param->pucBuffer) { + // connection to be closed, final calling, cleanup + ctx->file.close(); + delete ctx; + param->hs->ptr = 0; + return 0; + } + } else { + int id = 0; + if (param->pucRequest[0] == '/') { + id = atoi(param->pucRequest + 1); + } + sprintf(param->pucBuffer, "/DATA/%u.CSV", id == 0 ? fileid : id); + ctx = new LogDataContext; +#if STORAGE == STORAGE_SPIFFS + ctx->file = SPIFFS.open(param->pucBuffer, FILE_READ); +#else + ctx->file = SD.open(param->pucBuffer, FILE_READ); +#endif + if (!ctx->file) { + strcat(param->pucBuffer, " not found"); + param->contentLength = strlen(param->pucBuffer); + delete ctx; + return FLAG_DATA_RAW; + } + param->hs->ptr = (void*)ctx; + } + + if (!ctx->file.available()) { + // EOF + return 0; + } + param->contentLength = ctx->file.readBytes(param->pucBuffer, param->bufSize); + param->contentType = HTTPFILETYPE_TEXT; + return FLAG_DATA_STREAM; +} + +int handlerLogData(UrlHandlerParam* param) +{ + uint32_t duration = 0; + LogDataContext* ctx = (LogDataContext*)param->hs->ptr; + param->contentType = HTTPFILETYPE_JSON; + if (ctx) { + if (!param->pucBuffer) { + // connection to be closed, final calling, cleanup + ctx->file.close(); + delete ctx; + param->hs->ptr = 0; + return 0; + } + } else { + int id = 0; + if (param->pucRequest[0] == '/') { + id = atoi(param->pucRequest + 1); + } + sprintf(param->pucBuffer, "/DATA/%u.CSV", id == 0 ? fileid : id); + ctx = new LogDataContext; +#if STORAGE == STORAGE_SPIFFS + ctx->file = SPIFFS.open(param->pucBuffer, FILE_READ); +#else + ctx->file = SD.open(param->pucBuffer, FILE_READ); +#endif + if (!ctx->file) { + param->contentLength = sprintf(param->pucBuffer, "{\"error\":\"Data file not found\"}"); + delete ctx; + return FLAG_DATA_RAW; + } + ctx->pid = mwGetVarValueHex(param->pxVars, "pid", 0); + ctx->tsStart = mwGetVarValueInt(param->pxVars, "start", 0); + ctx->tsEnd = 0xffffffff; + duration = mwGetVarValueInt(param->pxVars, "duration", 0); + if (ctx->tsStart && duration) { + ctx->tsEnd = ctx->tsStart + duration; + duration = 0; + } + param->hs->ptr = (void*)ctx; + // JSON head + param->contentLength = sprintf(param->pucBuffer, "["); + } + + int len = 0; + char buf[64]; + uint32_t ts = 0; + + for (;;) { + int c = ctx->file.read(); + if (c == -1) { + if (param->contentLength == 0) { + // EOF + return 0; + } + // JSON tail + if (param->pucBuffer[param->contentLength - 1] == ',') param->contentLength--; + param->pucBuffer[param->contentLength++] = ']'; + break; + } + if (c == '\n') { + // line end, process the line + buf[len] = 0; + char *value = strchr(buf, ','); + if (value++) { + uint16_t pid = hex2uint16(buf); + if (pid == 0) { + // timestamp + ts = atoi(value); + if (duration) { + ctx->tsEnd = ts + duration; + duration = 0; + } + } else if (pid == ctx->pid && ts >= ctx->tsStart && ts < ctx->tsEnd) { + // generate json array element + param->contentLength += snprintf(param->pucBuffer + param->contentLength, param->bufSize - param->contentLength, + "[%u,%s],", ts, value); + } + } + len = 0; + if (param->contentLength + 32 > param->bufSize) break; + } else if (len < sizeof(buf) - 1) { + buf[len++] = c; + } + } + return FLAG_DATA_STREAM; +} + +int handlerLogList(UrlHandlerParam* param) +{ + char *buf = param->pucBuffer; + int bufsize = param->bufSize; + File file; +#if STORAGE == STORAGE_SPIFFS + File root = SPIFFS.open("/"); +#elif STORAGE == STORAGE_SD + File root = SD.open("/DATA"); +#endif + int n = snprintf(buf, bufsize, "["); + if (root) { + while(file = root.openNextFile()) { + const char *fn = file.name(); + if (!strncmp(fn, "/DATA/", 6)) { + fn += 6; + unsigned int size = file.size(); + Serial.print(fn); + Serial.print(' '); + Serial.print(size); + Serial.println(" bytes"); + unsigned int id = atoi(fn); + if (id) { + n += snprintf(buf + n, bufsize - n, "{\"id\":%u,\"size\":%u", + id, size); + if (id == fileid) { + n += snprintf(buf + n, bufsize - n, ",\"active\":true"); + } + n += snprintf(buf + n, bufsize - n, "},"); + } + } + } + if (buf[n - 1] == ',') n--; + } + n += snprintf(buf + n, bufsize - n, "]"); + param->contentType=HTTPFILETYPE_JSON; + param->contentLength = n; + return FLAG_DATA_RAW; +} + +int handlerLogDelete(UrlHandlerParam* param) +{ + int id = 0; + if (param->pucRequest[0] == '/') { + id = atoi(param->pucRequest + 1); + } + sprintf(param->pucBuffer, "/DATA/%u.CSV", id); + if (id == fileid) { + strcat(param->pucBuffer, " still active"); + } else { +#if STORAGE == STORAGE_SPIFFS + bool removal = SPIFFS.remove(param->pucBuffer); +#else + bool removal = SD.remove(param->pucBuffer); +#endif + if (removal) { + strcat(param->pucBuffer, " deleted"); + } else { + strcat(param->pucBuffer, " not found"); + } + } + param->contentLength = strlen(param->pucBuffer); + param->contentType = HTTPFILETYPE_TEXT; + return FLAG_DATA_RAW; +} + +UrlHandler urlHandlerList[]={ + {"api/live", handlerLiveData}, + {"api/info", handlerInfo}, +#if STORAGE != STORAGE_NONE + {"api/list", handlerLogList}, + {"api/data", handlerLogData}, + {"api/log", handlerLogFile}, + {"api/delete", handlerLogDelete}, +#endif + {0} +}; + +void obtainTime() +{ + sntp_setoperatingmode(SNTP_OPMODE_POLL); + sntp_setservername(0, (char*)"pool.ntp.org"); + sntp_init(); +} + +void serverProcess(int timeout) +{ + mwHttpLoop(&httpParam, timeout); +} + +bool serverSetup(IPAddress& ip) +{ +#if NET_DEVICE == NET_WIFI + WiFi.mode (WIFI_AP_STA); +#else + WiFi.mode (WIFI_AP); +#endif + + WiFi.softAP(WIFI_AP_SSID, WIFI_AP_PASSWORD); + ip = WiFi.softAPIP(); + + mwInitParam(&httpParam, 80, "/spiffs"); + httpParam.pxUrlHandler = urlHandlerList; + httpParam.maxClients = 4; + + if (mwServerStart(&httpParam)) { + return false; + } + +#if NET_DEVICE == NET_WIFI + obtainTime(); +#endif + return true; +} + +#else + +void serverProcess(int timeout) +{ + delay(timeout); +} + +#endif diff --git a/esp32/telelogger/platformio.ini b/esp32/telelogger/platformio.ini new file mode 100644 index 0000000..8b13c72 --- /dev/null +++ b/esp32/telelogger/platformio.ini @@ -0,0 +1,34 @@ +; 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 +; http://docs.platformio.org/page/projectconf.html + +[env:esp32dev] +platform = espressif32 +; build_flags = -DBOARD_HAS_PSRAM +; build_flags = -DCORE_DEBUG_LEVEL=5 -DSTACK_SIZE=16384 +board=esp-wrover-kit +board_build.f_cpu = 160000000L +framework = arduino +monitor_speed = 115200 +board_build.flash_mode = qio +board_build.partitions = huge_app.csv + +;[env:esp32c3] +;platform = espressif32 +;framework = arduino +;board = esp32-c3-devkitm-1 +;board_build.mcu = esp32c3 +;monitor_speed=115200 +;board_build.partitions = huge_app.csv + +[platformio] +src_dir=. + +[env] +lib_extra_dirs=../../libraries diff --git a/esp32/telelogger/teleclient.cpp b/esp32/telelogger/teleclient.cpp new file mode 100644 index 0000000..4119266 --- /dev/null +++ b/esp32/telelogger/teleclient.cpp @@ -0,0 +1,737 @@ +/****************************************************************************** +* Freematics Hub client and Traccar client implementations +* Works with Freematics ONE+ +* Developed by Stanley Huang +* Distributed under BSD license +* Visit https://freematics.com/products for hardware information +* Visit https://hub.freematics.com to view live and history telemetry data +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ + +#include +#include "telestore.h" +#include "telemesh.h" +#include "teleclient.h" +#include "config.h" + +#if SERVER_ENCRYPTION_ENABLE == 1 +#include "telecrypt.h" +#endif + +extern int16_t rssi; +extern char devid[]; +extern char vin[]; +extern GPS_DATA* gd; +extern char isoTime[]; + +CBuffer::CBuffer(uint8_t* mem) +{ + m_data = mem; + purge(); +} + +void CBuffer::add(uint16_t pid, uint8_t type, void* values, int bytes, uint8_t count) +{ + if (offset < BUFFER_LENGTH - sizeof(ELEMENT_HEAD) - bytes) { + ELEMENT_HEAD hdr = {pid, type, count}; + *(ELEMENT_HEAD*)(m_data + offset) = hdr; + offset += sizeof(ELEMENT_HEAD); + memcpy(m_data + offset, values, bytes); + offset += bytes; + total++; + } else { + Serial.println("FULL"); + } +} + +void CBuffer::purge() +{ + state = BUFFER_STATE_EMPTY; + timestamp = 0; + offset = 0; + total = 0; +} + +void CBuffer::serialize(CStorage& store) +{ + uint16_t of = 0; + for (int n = 0; n < total && of < offset; n++) { + ELEMENT_HEAD* hdr = (ELEMENT_HEAD*)(m_data + of); + of += sizeof(ELEMENT_HEAD); + switch (hdr->type) { + case ELEMENT_UINT8: + store.log(hdr->pid, (uint8_t*)(m_data + of), hdr->count); + of += (uint16_t)hdr->count * sizeof(uint8_t); + break; + case ELEMENT_UINT16: + store.log(hdr->pid, (uint16_t*)(m_data + of), hdr->count); + of += (uint16_t)hdr->count * sizeof(uint16_t); + break; + case ELEMENT_UINT32: + store.log(hdr->pid, (uint32_t*)(m_data + of), hdr->count); + of += (uint16_t)hdr->count * sizeof(uint32_t); + break; + case ELEMENT_INT32: + store.log(hdr->pid, (int32_t*)(m_data + of), hdr->count); + of += (uint16_t)hdr->count * sizeof(int32_t); + break; + case ELEMENT_FLOAT: + store.log(hdr->pid, (float*)(m_data + of), hdr->count); + of += (uint16_t)hdr->count * sizeof(float); + break; + case ELEMENT_FLOAT_D1: + store.log(hdr->pid, (float*)(m_data + of), hdr->count, "%.1f"); + of += (uint16_t)hdr->count * sizeof(float); + break; + case ELEMENT_FLOAT_D2: + store.log(hdr->pid, (float*)(m_data + of), hdr->count, "%.2f"); + of += (uint16_t)hdr->count * sizeof(float); + break; + default: + return; + } + } +} + +void CBufferManager::init() +{ + total = BUFFER_SLOTS; +#if BOARD_HAS_PSRAM + slots = (CBuffer**)heap_caps_malloc(BUFFER_SLOTS * sizeof(void*), MALLOC_CAP_SPIRAM); +#else + slots = (CBuffer**)malloc(BUFFER_SLOTS * sizeof(void*)); +#endif + for (int n = 0; n < BUFFER_SLOTS; n++) { + void* mem; +#if BOARD_HAS_PSRAM + mem = heap_caps_malloc(BUFFER_LENGTH, MALLOC_CAP_SPIRAM); +#else + mem = malloc(BUFFER_LENGTH); +#endif + if (!mem) { + Serial.println("OUT OF RAM"); + total = n; + break; + } + slots[n] = new CBuffer((uint8_t*)mem); + } + assert(total > 0); +} + +void CBufferManager::purge() +{ + for (int n = 0; n < total; n++) slots[n]->purge(); +} + +CBuffer* CBufferManager::getFree() +{ + if (last) { + CBuffer* slot = last; + last = 0; + if (slot->state == BUFFER_STATE_EMPTY) return slot; + } + uint32_t ts = 0xffffffff; + int m = 0; + // search for free slot, if none, mark the oldest one + for (int n = 0; n < total; n++) { + if (slots[n]->state == BUFFER_STATE_EMPTY) { + return slots[n]; + } else if (slots[n]->state == BUFFER_STATE_FILLED && slots[n]->timestamp < ts) { + m = n; + ts = slots[n]->timestamp; + } + } + // dispose oldest data when buffer is full + while (slots[m]->state == BUFFER_STATE_LOCKED) delay(1); + slots[m]->purge(); + return slots[m]; +} + +CBuffer* CBufferManager::getOldest() +{ + uint32_t ts = 0xffffffff; + int m = -1; + for (int n = 0; n < total; n++) { + if (slots[n]->state == BUFFER_STATE_FILLED && slots[n]->timestamp < ts) { + m = n; + ts = slots[n]->timestamp; + } + } + if (m >= 0) { + slots[m]->state = BUFFER_STATE_LOCKED; + return slots[m]; + } + return 0; +} + +CBuffer* CBufferManager::getNewest() +{ + uint32_t ts = 0; + int m = -1; + for (int n = 0; n < total; n++) { + if (slots[n]->state == BUFFER_STATE_FILLED && slots[n]->timestamp > ts) { + m = n; + ts = slots[n]->timestamp; + } + } + if (m >= 0) { + slots[m]->state = BUFFER_STATE_LOCKED; + return slots[m]; + } + return 0; +} + +void CBufferManager::free(CBuffer* slot) +{ + slot->purge(); + last = slot; +} + +void CBufferManager::printStats() +{ + int bytes = 0; + int count = 0; + int samples = 0; + for (int n = 0; n < total; n++) { + if (slots[n]->state != BUFFER_STATE_FILLED) continue; + bytes += slots[n]->offset; + samples += slots[n]->total; + count++; + } + if (slots) { + Serial.print("[BUF] "); + Serial.print(samples); + Serial.print(" samples | "); + Serial.print(bytes); + Serial.print(" bytes | "); + Serial.print(count); + Serial.print('/'); + Serial.println(total); + } +} + +bool TeleClientUDP::verifyChecksum(char* data) +{ + uint8_t sum = 0; + char *s = strrchr(data, '*'); + if (!s) return false; + for (char *p = data; p < s; p++) sum += *p; + if (hex2uint8(s + 1) == sum) { + *s = 0; + return true; + } + return false; +} + +bool TeleClientUDP::notify(byte event, const char* payload) +{ + char buf[48]; + char cache[128]; + CStorageRAM netbuf; + netbuf.init(cache, 128); + netbuf.header(devid); + netbuf.dispatch(buf, sprintf(buf, "EV=%X", (unsigned int)event)); + netbuf.dispatch(buf, sprintf(buf, "TS=%lu", millis())); + netbuf.dispatch(buf, sprintf(buf, "ID=%s", devid)); + if (rssi) { + netbuf.dispatch(buf, sprintf(buf, "SSI=%d", (int)rssi)); + } + if (vin[0]) { + netbuf.dispatch(buf, sprintf(buf, "VIN=%s", vin)); + } + if (payload) { + netbuf.dispatch(payload, strlen(payload)); + } + netbuf.tailer(); + //Serial.println(netbuf.buffer()); + for (byte attempts = 0; attempts < 3; attempts++) { + // send notification datagram +#if ENABLE_WIFI + if (wifi.connected()) + { + #if SERVER_ENCRYPTION_ENABLE == 1 + char *orig_send_buf = netbuf.buffer(); + unsigned int orig_send_buf_len = netbuf.length(); + unsigned char encrypted_buf[12 + orig_send_buf_len + 16]; + encrypt_string((unsigned char *)orig_send_buf, orig_send_buf_len, encrypted_buf); + if (!wifi.send((const char *)encrypted_buf, sizeof(encrypted_buf))) break; + #else + if (!wifi.send(netbuf.buffer(), netbuf.length())) break; + #endif + } + else +#endif + { + #if SERVER_ENCRYPTION_ENABLE == 1 + char *orig_send_buf = netbuf.buffer(); + unsigned int orig_send_buf_len = netbuf.length(); + unsigned char encrypted_buf[12 + orig_send_buf_len + 16]; + encrypt_string((unsigned char *)orig_send_buf, orig_send_buf_len, encrypted_buf); + if (!cell.send((const char *)encrypted_buf, sizeof(encrypted_buf))) break; + #else + if (!cell.send(netbuf.buffer(), netbuf.length())) break; + #endif + } + if (event == EVENT_ACK) return true; // no reply for ACK + char *data = 0; + int bytesRecv = 0; + // receive reply +#if ENABLE_WIFI + if (wifi.connected()) + { + data = cell.getBuffer(); + bytesRecv = wifi.receive(data, RECV_BUF_SIZE - 1); + if (bytesRecv > 0) { + data[bytesRecv] = 0; + } + } + else +#endif + { + data = cell.receive(&bytesRecv); + } + if (!data || bytesRecv == 0) { + Serial.println("[UDP] Timeout"); + continue; + } + rxBytes += bytesRecv; + + // decrypt received data +#if SERVER_ENCRYPTION_ENABLE == 1 + Serial.println("decrypting data"); + + if (bytesRecv >= 12 + 16) { + char decrypted_data[bytesRecv - 12 - 16 + 1]; // +1 for null-terminator + decrypt_string((unsigned char *)data, bytesRecv, (unsigned char *)decrypted_data); + Serial.println("decrytion function exited"); + if (decrypted_data[0] == '\0') { + continue; + } + data = decrypted_data; + bytesRecv = strlen(decrypted_data); + } else { + Serial.println("[CHACHA] Received data is too short to be decrypted"); + continue; + } + + Serial.println("decrypted data"); +#endif + + // verify checksum + if (!verifyChecksum(data)) { + Serial.print("[UDP] Checksum mismatch:"); + Serial.println(data); + continue; + } + char pattern[16]; + sprintf(pattern, "EV=%u", event); + if (!strstr(data, pattern)) { + Serial.print("[UDP] Invalid reply: "); + Serial.println(data); + continue; + } + if (event == EVENT_LOGIN) { + // extract info from server response + char *p = strstr(data, "TM="); + if (p) { + // set local time from server + unsigned long tm = atol(p + 3); + struct timeval tv = { .tv_sec = (time_t)tm, .tv_usec = 0 }; + settimeofday(&tv, NULL); + } + p = strstr(data, "SN="); + if (p) { + char *q = strchr(p, ','); + if (q) *q = 0; + } + feedid = hex2uint16(data); + login = true; + } else if (event == EVENT_LOGOUT) { + login = false; + } + // success + return true; + } + return false; +} + +bool TeleClientUDP::connect(bool quick) +{ + byte event = login ? EVENT_RECONNECT : EVENT_LOGIN; + bool success = false; +#if ENABLE_WIFI + if (wifi.connected()) + { + if (quick) return wifi.open(SERVER_HOST, SERVER_PORT); + } + else +#endif + { + cell.close(); + if (quick) { + return cell.open(0, 0); + } + } + + packets = 0; + + // connect to telematics server + for (byte attempts = 0; attempts < 3; attempts++) { + Serial.print(event == EVENT_LOGIN ? "LOGIN(" : "RECONNECT("); + Serial.print(SERVER_HOST); + Serial.print(':'); + Serial.print(SERVER_PORT); + Serial.println(")..."); +#if ENABLE_WIFI + if (wifi.connected()) + { + if (!wifi.open(SERVER_HOST, SERVER_PORT)) { + Serial.println("[WIFI] Unable to connect"); + delay(1000); + continue; + } + } + else +#endif + { + if (!cell.open(SERVER_HOST, SERVER_PORT)) { + if (!cell.check()) break; + Serial.println("[NET] Unable to connect"); + delay(3000); + continue; + } + } + // log in or reconnect to Freematics Hub + if (!notify(event)) { +#if ENABLE_WIFI + if (wifi.connected()) + { + wifi.close(); + } + else +#endif + { + if (!cell.check()) break; + cell.close(); + } + Serial.println("[NET] Server timeout"); + continue; + } + success = true; + break; + } + if (event == EVENT_LOGIN) startTime = millis(); + if (success) { + lastSyncTime = millis(); + } + return success; +} + +bool TeleClientUDP::ping() +{ + bool success = false; + for (byte n = 0; n < 3 && !success; n++) { +#if ENABLE_WIFI + if (wifi.connected()) + { + success = wifi.open(SERVER_HOST, SERVER_PORT); + } + else +#endif + { + success = cell.open(SERVER_HOST, SERVER_PORT); + } + if (success) { + if ((success = notify(EVENT_PING))) break; +#if ENABLE_WIFI + if (wifi.connected()) + { + wifi.close(); + } + else +#endif + { + cell.close(); + } + delay(1000); + } + } + if (success) lastSyncTime = millis(); + return success; +} + +bool TeleClientUDP::transmit(const char* packetBuffer, unsigned int packetSize) +{ +#if ENABLE_WIFI + // transmit data via wifi + if (wifi.connected()) { + if (wifi.send(packetBuffer, packetSize)) { + txBytes += packetSize; + txCount++; + Serial.print("[WIFI] "); + Serial.print(packetSize); + Serial.println(" bytes sent"); + return true; + } + return false; + } +#endif + + // transmit data via cellular + if (++packets >= 64) { + cell.close(); + cell.open(0, 0); + packets = 0; + } + Serial.print("[CELL] "); + Serial.print(packetSize); + Serial.println(" bytes being sent"); + if (cell.send(packetBuffer, packetSize)) { + txBytes += packetSize; + txCount++; + return true; + } + return false; +} + +void TeleClientUDP::inbound() +{ + // check incoming datagram + do { + int len = 0; + char *data = 0; +#if ENABLE_WIFI + if (wifi.connected()) + { + data = cell.getBuffer(); + len = wifi.receive(data, RECV_BUF_SIZE - 1, 10); + } + else +#endif + { + data = cell.receive(&len, 50); + } + if (!data || len == 0) break; + data[len] = 0; + Serial.print("[UDP] "); + Serial.println(data); + rxBytes += len; + if (!verifyChecksum(data)) { + Serial.print("[UDP] Checksum mismatch:"); + Serial.println(data); + break; + } + char *p = strstr(data, "EV="); + if (!p) break; + int eventID = atoi(p + 3); + switch (eventID) { + case EVENT_SYNC: + feedid = hex2uint16(data); + Serial.print("[UDP] FEED ID:"); + Serial.println(feedid); + break; + } + lastSyncTime = millis(); + } while(0); +} + +void TeleClientUDP::shutdown() +{ + if (login) { + notify(EVENT_LOGOUT); + login = false; + Serial.println("[NET] Logout"); + } +#if ENABLE_WIFI + if (wifi.connected()) { + wifi.end(); + Serial.println("[WIFI] Deactivated"); + return; + } +#endif + cell.end(); + Serial.println("[CELL] Deactivated"); +} + +bool TeleClientHTTP::notify(byte event, const char* payload) +{ + char url[256]; + snprintf(url, sizeof(url), "%s/notify/%s?EV=%u&SSI=%d&VIN=%s", SERVER_PATH, devid, + (unsigned int)event, (int)rssi, vin); + if (event == EVENT_LOGOUT) login = false; +#if ENABLE_WIFI + if (wifi.connected()) + { + return wifi.send(METHOD_GET, url, true) && wifi.receive(cell.getBuffer(), RECV_BUF_SIZE - 1) && wifi.code() == 200; + } + else +#endif + { + return cell.send(METHOD_GET, url, true) && cell.receive() && cell.code() == 200; + } +} + +bool TeleClientHTTP::transmit(const char* packetBuffer, unsigned int packetSize) +{ +#if ENABLE_WIFI + if ((wifi.connected() && wifi.state() != HTTP_CONNECTED) || cell.state() != HTTP_CONNECTED) { +#else + if (cell.state() != HTTP_CONNECTED) { +#endif + // reconnect if disconnected + if (!connect(true)) { + return false; + } + } + + char url[256]; + bool success = false; + int len; +#if SERVER_METHOD == PROTOCOL_METHOD_GET + if (gd && gd->ts) { + len = snprintf(url, sizeof(url), "%s/push?id=%s×tamp=%s&lat=%f&lon=%f&altitude=%d&speed=%f&heading=%d", + SERVER_PATH, devid, isoTime, + gd->lat, gd->lng, (int)gd->alt, gd->speed, (int)gd->heading); + } else { + len = snprintf(url, sizeof(url), "%s/push?id=%s", SERVER_PATH, devid); + } + success = cell.send(METHOD_GET, url, true); +#else + len = snprintf(url, sizeof(url), "%s/post/%s", SERVER_PATH, devid); +#if ENABLE_WIFI + if (wifi.connected()) { + Serial.print("[WIFI] "); + Serial.println(url); + success = wifi.send(METHOD_POST, url, true, packetBuffer, packetSize); + } + else +#endif + { + Serial.print("[CELL] "); + Serial.println(url); + success = cell.send(METHOD_POST, url, true, packetBuffer, packetSize); + } + len += packetSize; +#endif + if (!success) { + Serial.println("Connection closed"); + return false; + } else { + txBytes += len; + txCount++; + } + + // check response + int recvBytes = 0; + char* content = 0; +#if ENABLE_WIFI + if (wifi.connected()) + { + content = wifi.receive(cell.getBuffer(), RECV_BUF_SIZE - 1, &recvBytes); + } + else +#endif + { + content = cell.receive(&recvBytes); + } + if (!content) { + // close connection on receiving timeout + Serial.println("No HTTP response"); + return false; + } + Serial.print("[HTTP] "); + Serial.println(content); +#if ENABLE_WIFI + if ((wifi.connected() && wifi.code() == 200) || cell.code() == 200) { +#else + if (cell.code() == 200) { +#endif + // successful + lastSyncTime = millis(); + rxBytes += recvBytes; + } + return true; +} + +bool TeleClientHTTP::connect(bool quick) +{ + if (!quick) { +#if ENABLE_WIFI + if (!wifi.connected()) cell.init(); +#else + cell.init(); +#endif + } else { +#if ENABLE_WIFI + if (!wifi.connected()) cell.close(); +#else + cell.close(); +#endif + } + + // connect to HTTP server + bool success = false; + +#if ENABLE_WIFI + if (wifi.connected()) success = wifi.open(SERVER_HOST, SERVER_PORT); +#endif + if (!success) { + for (byte attempts = 0; !success && attempts < 3; attempts++) { + success = cell.open(SERVER_HOST, SERVER_PORT); + if (!success) { + if (!cell.check()) break; + cell.close(); + cell.init(); + } + } + } + if (!success) { + Serial.println("[CELL] Unable to connect"); + return false; + } + if (quick) return true; + if (!login) { + Serial.print("LOGIN("); + Serial.print(SERVER_HOST); + Serial.print(':'); + Serial.print(SERVER_PORT); + Serial.println(")..."); + // log in or reconnect to Freematics Hub + if (notify(EVENT_LOGIN)) { + lastSyncTime = millis(); + login = true; + } + } + return true; +} + +bool TeleClientHTTP::ping() +{ + return connect(); +} + +void TeleClientHTTP::shutdown() +{ + if (login) { + notify(EVENT_LOGOUT); + login = false; + Serial.println("[NET] Logout"); + } +#if ENABLE_WIFI + if (wifi.connected()) { + wifi.end(); + Serial.println("[WIFI] Deactivated"); + return; + } +#endif + cell.close(); + cell.end(); + Serial.println("[CELL] Deactivated"); +} diff --git a/esp32/telelogger/teleclient.h b/esp32/telelogger/teleclient.h new file mode 100644 index 0000000..6b3631c --- /dev/null +++ b/esp32/telelogger/teleclient.h @@ -0,0 +1,114 @@ +#include "config.h" + +#define EVENT_LOGIN 1 +#define EVENT_LOGOUT 2 +#define EVENT_SYNC 3 +#define EVENT_RECONNECT 4 +#define EVENT_COMMAND 5 +#define EVENT_ACK 6 +#define EVENT_PING 7 + +#define BUFFER_STATE_EMPTY 0 +#define BUFFER_STATE_FILLING 1 +#define BUFFER_STATE_FILLED 2 +#define BUFFER_STATE_LOCKED 3 + +#define ELEMENT_UINT8 0 +#define ELEMENT_UINT16 1 +#define ELEMENT_UINT32 2 +#define ELEMENT_INT32 3 +#define ELEMENT_FLOAT 4 +#define ELEMENT_FLOAT_D1 5 /* floating-point data with 1 decimal place*/ +#define ELEMENT_FLOAT_D2 6 /* floating-point data with 2 decimal places*/ + +typedef struct { + uint16_t pid; + uint8_t type; + uint8_t count; +} ELEMENT_HEAD; + +class CBuffer +{ +public: + CBuffer(uint8_t* mem); + void add(uint16_t pid, uint8_t type, void* values, int bytes, uint8_t count = 1); + void purge(); + void serialize(CStorage& store); + uint32_t timestamp; + uint16_t offset; + uint8_t total; + uint8_t state; +private: + uint8_t* m_data; +}; + +class CBufferManager +{ +public: + void init(); + void purge(); + void free(CBuffer* slot); + CBuffer* getFree(); + CBuffer* getOldest(); + CBuffer* getNewest(); + void printStats(); +private: + CBuffer** slots = 0; + CBuffer* last = 0; + uint32_t total = 0; +}; + +class TeleClient +{ +public: + virtual void reset() + { + txCount = 0; + txBytes = 0; + rxBytes = 0; + login = false; + startTime = millis(); + } + virtual bool notify(byte event, const char* payload = 0) { return true; } + virtual bool connect() { return true; } + virtual bool transmit(const char* packetBuffer, unsigned int packetSize) { return true; } + virtual void inbound() {} + uint32_t txCount = 0; + uint32_t txBytes = 0; + uint32_t rxBytes = 0; + uint32_t lastSyncTime = 0; + uint16_t feedid = 0; + uint32_t startTime = 0; + uint8_t packets = 0; + bool login = false; +}; + +class TeleClientUDP : public TeleClient +{ +public: + bool notify(byte event, const char* payload = 0); + bool connect(bool quick = false); + bool transmit(const char* packetBuffer, unsigned int packetSize); + bool ping(); + void inbound(); + bool verifyChecksum(char* data); + void shutdown(); +#if ENABLE_WIFI + WifiUDP wifi; +#endif + CellUDP cell; +}; + +class TeleClientHTTP : public TeleClient +{ +public: + bool notify(byte event, const char* payload = 0); + bool connect(bool quick = false); + bool transmit(const char* packetBuffer, unsigned int packetSize); + bool ping(); + void shutdown(); +#if ENABLE_WIFI + WifiHTTP wifi; +#endif + CellHTTP cell; +}; \ No newline at end of file diff --git a/esp32/telelogger/telecrypt.cpp b/esp32/telelogger/telecrypt.cpp new file mode 100644 index 0000000..30b87fa --- /dev/null +++ b/esp32/telelogger/telecrypt.cpp @@ -0,0 +1,120 @@ +#include "config.h" +#include +#include +#include +#include + +void print_hex(const unsigned char *data, size_t length) { + for (size_t i = 0; i < length; ++i) { + Serial.printf("%02x", data[i]); + } + Serial.println(); +} + +void encrypt_string(const unsigned char *input, size_t length, unsigned char *output) { + // Create an instance of the ChaChaPoly class + ChaChaPoly chachaPoly; + + // Initialize the encryption key + unsigned char key[32]; + for (int i = 0; i < 32; ++i) { + sscanf(CHACHA20_KEY + 2*i, "%02x", &key[i]); + } + + // Set the encryption key + chachaPoly.setKey(key, sizeof(key)); + + // Generate a random nonce (IV) + unsigned char nonce[12]; + esp_fill_random(nonce, sizeof(nonce)); // Use the ESP-IDF random number generator + chachaPoly.setIV(nonce, sizeof(nonce)); + + // Encrypt the input data + chachaPoly.encrypt(output + sizeof(nonce), input, length); + + // Compute the authentication tag + chachaPoly.computeTag(output + sizeof(nonce) + length, chachaPoly.tagSize()); + + // Prepend the nonce to the output + memcpy(output, nonce, sizeof(nonce)); + + // Clear the encryption context + chachaPoly.clear(); +} + +void decrypt_string(const unsigned char *input, size_t length, unsigned char *output) { + // Create an instance of the ChaChaPoly class + ChaChaPoly chachaPoly; + + // Initialize the decryption key + unsigned char key[32]; + for (int i = 0; i < 32; ++i) { + sscanf(CHACHA20_KEY + 2*i, "%02x", &key[i]); + } + + Serial.println("loaded key"); + + // Set the decryption key + chachaPoly.setKey(key, sizeof(key)); + + Serial.println("set key"); + + // Extract the nonce (IV) from the input + unsigned char nonce[12]; + memcpy(nonce, input, sizeof(nonce)); + chachaPoly.setIV(nonce, sizeof(nonce)); + + // Check that length is long enough to contain a nonce and a tag. + if (length < sizeof(nonce) + chachaPoly.tagSize()) { + Serial.print("[CHACHA] Input too short to contain nonce and tag: "); + print_hex(input, length); + output[0] = '\0'; // Set output to an empty string + return; + } + + Serial.println("did nonce"); + + // Decrypt the input data + size_t decryptedLength = length - sizeof(nonce) - chachaPoly.tagSize(); + chachaPoly.decrypt(output, input + sizeof(nonce), decryptedLength); + + Serial.println("did decryption"); + + // Print the decrypted data as hex values + String decryptedString = ""; + for (size_t i = 0; i < decryptedLength; i++) { + decryptedString += (char)output[i]; + } + Serial.println(decryptedString); + + // Verify the authentication tag + + + const unsigned char *tagPtr = input + sizeof(nonce) + decryptedLength; + Serial.print("Tag: "); + for (size_t i = 0; i < chachaPoly.tagSize(); i++) { + Serial.print(tagPtr[i], HEX); + Serial.print(" "); + } + Serial.println(); + + Serial.print("Computed Tag: "); + uint8_t computedTag[16]; + chachaPoly.computeTag(computedTag, sizeof(computedTag)); + for (size_t i = 0; i < sizeof(computedTag); i++) { + Serial.print(computedTag[i], HEX); + Serial.print(" "); + } + Serial.println(); + + if (!chachaPoly.checkTag(tagPtr, chachaPoly.tagSize())) { + Serial.println("Authentication failed!"); + output[0] = '\0'; // Set output to an empty string + return; + } + + /// + + // Clear the decryption context + chachaPoly.clear(); +} \ No newline at end of file diff --git a/esp32/telelogger/telecrypt.h b/esp32/telelogger/telecrypt.h new file mode 100644 index 0000000..37098b4 --- /dev/null +++ b/esp32/telelogger/telecrypt.h @@ -0,0 +1,9 @@ +#include "config.h" +#include +#include +#include +#include + +void encrypt_string(const unsigned char *input, size_t length, unsigned char *output); +void decrypt_string(const unsigned char *input, size_t length, unsigned char *output); +void print_hex(const unsigned char *data, size_t length); \ No newline at end of file diff --git a/esp32/telelogger/telelogger.bin b/esp32/telelogger/telelogger.bin new file mode 100644 index 0000000000000000000000000000000000000000..06f8d776b1e260389399956f25856445fd4d6795 GIT binary patch literal 380176 zcmdqK3t*ddxj+6*+I6K{*D}V{LEnvaw$Mv@*{y8rn@3&u93Gw);)#~p>!Dmr~+IwS{MRW$fcdL;y5e-E$?Xk#sYdRW_ zhBDFi>F&1fR$J@NP-xBCE)hycrecX?YdkjAIyM!HN1|z?b|_*6>sr00b!WJ9T~~KkbZlL>&il`R;nY+xlMSV_!lg7dPBklynI2_iDv^jK zb}Gq)k^ziIm2fg0Ri;DfSSXQIlH*E92~WgQEy~zbRtcq2N-P6yES^<%$FdX3#!4#e?JN=$nE&quHirW!VO0B;W{c4cLcB+=W5= zXuvDl-Om2@aB?!0OhgmeOnWqw3TF0XveC(Q@>hE%875p;XFFuJg+)4=5lfaVnNNJ= zN-Pn}is|u8Fq%#)8#X8|zc1+Bq^w-2EbkA+$a~qOl8)|-L1bD1BcdviNyekg=W?23 zd@2R^+9T0v_>Zl$i``-_XUH!~Dm#%z9#tljk*Roe*>W)+4`n8T>1a0ZJrPL;DfXrB zMfI{;_2DzB)!G_=HiU@JOu`1GZz`S4M#EWU93h!XN0-&s)YgdFn!a>$cOr?DjYNr@ zOe?!X86}&J?c5nnM{Ob{LR(uK$Bk>K zUl~WbEB--y=c?5`iWrHGC6gpVvWzBnB@lgLI69e3?@=OClPOK0Gdec4Q;E)mqbc^5 z3@jx0VL2Z4XQL@a4DRmBdql|e3WpLQh}s>>h9{&Cb7?_=P{fuz#g`R{Q5uDm-tqMTlbNfdqQ)i6>LH3GE_%9YD9 zR1T*S6UdpUdD?`Q*N}izY+%$VD7Xb#O*oTNs7mvB&%nAXnodOH29hI*Y9SoWWKiyN znN=XGplX^*p@xrSm27M>N)-eOm%}$|o0Hlh9HH<;REfkgvOcFaBTOh%VzQ7>{RvT_ zFgY8I2zNXl-5H9zDdSFssjvwDo=i9tkHe)Lu0M+Mz-3zuB|{NEDypbvj7~@69XT%x zdpw>DhuD{>!JM(_SR~rF=i+EODSVF2q4-o(_|m|_Rk)rLW{<~qCeZX`rK>)tJrYTq z9Q3*SouO<uM8Upy9Kwforz?GZg_ z1Z*>nwzXTKBOsK$>s!$~85#8jTu#m83AjC@EM_f-BDms?U^+9P9P3~gR#j}JQxp!(LNTr(WnI6j!pic z&ovSp^>gdXPTGBa3U!jEqKWVxr3uyf@Ze?5N@gNC6^|%mR4k`snHbd$V|!SJCSrL$ zMC`D!|3|>&8$=QFj9tqUmfhBfEMt*=QmYVm)FI zAN;J?oq>{2NQiT9t7nP zq41lgV(F+z&G^uLcB4lc3&mp@^j;vH0*P;M4rKw2Qy6Xs(UfqSH)*2)>RboVeGG1K z27&?iu**9d2>M&lrJ0IqnMJo2ibR@XXa$3ar^!$(L5V@pmWl*MMqNcoT9^~4?}-lb zDglBYU2tnjPm7q-9Q>l0?%)QHjXx0sX1mH2*}dWGteb*Nli?)&v`=DM_S;1b#5_ppas$ghD&x#+b_=yDTc}re>v#b)=!ei4xY~ z2OH4MVn&yifz;%9lfr%^e~VJ?p==VGnX~|tTZ)zpkl{}B?a3k=wHtj)E;V5oY;r<5 zzl6nRL}8IQMDlQG#=+&9{0_5LY$R^|1~{j4Xi+*%krhbFNG}~DS^`t*=VF$U$DD$r z99NuD@I-2lFPT9eL2ru#-=q|zcPBB+1T)CjVhhqc48kbKBlG*IBZrY_n(|2$uA!N;2b2%#MXFF=EJ*~2eQM|@PUi9I$IMen{?Fe+_=F;BtP?kE zXhK(IO~3q<<`Q)}{J=Fj<|)zYtXDb(YHYp1m8EnW(=IXB8B}e{ztjveR%lN&Lg5&~ zOr0zho~g;PXxckY)hG8Iii!rxS1-#bs3fTp>?uaeIu1b~SM*wuBUxs9wke zNW#&?81>oUDWoN$7(ION6sK*zenbeSW<1f|7`!D>HuoU>O-kn~u7veUOLr+(|8f~O z)$8z^^jE10yOaga4Ia$+P+&B#k{7AJ%{4d|1ep^lFQsPm%G1%ejk6J4k~yHM`4Y)T zH4ztuXl5`Pk7$hg9Xz7r_3nV2b=U|-8v1o)x`P&ek znk>!cCFJ?bZKFeo>Lz6+)tg*s@<~OxgCZPt`*g0O21H@4C)~~MMV_SNnV{jdlb+dgOb1;+&n0k@nE#{GUF2nsvW~N-lnPmi% zEwVn%=R@fd#*|TSB9F~d4n|!Z&JMq)ru#56H5o-ECF2bhN;==akT~5ZsIw)4i?%?8E*lRnO*zAm zKgYxk3%Mw3Tv;djVu=JwV3Kkw_tsMw)W#CQNDO1jU<5z8UPvTEzG*J1cClcTYNwxI zIFzDlmqs^ph(Rlann_B-)Oi=@BpWaw7|bM*@)3>gQ=-ba}8$ zg3OS?e3rsCLNpb)Tol;`nuh8Sa2%WHNd>91i=M2F#c;4_VxH_9FytSDQDX>>Vfau2 z9f^*IFjh8DCRt!mCs16=>4ac58RY245HDS1K_b|P=r|9$?7pDieX$Fx4qK0tG(6Ho z`fyssA~Oo?C#EKYWX|NVDA#$cf5?r64cD+Qa8c7reU&E~k#?HdQYIKmr$c-6w331- zE6CXl-4a`&L25KT9gS>@UDC$+i9BIMA`_Gj1}~S$=~7Gq?Lndkr?Rm)Ww^Y33`#n5 z!5a(YvSXsazHvgw?{#4L#_t;0jF@x{dq*yUalq~MKnPFGrb7t~?RXk&MrqYGG?@@b zQ01zhiCQBt0hW%YkR^F3hr$gFiNkDR3r!S=*i>C!dbx@MKGS*>f<=$cNaX1z<-bnBW`x@NVmS;HF4DUuy5bQ8e$QAO>otQVrD zT1z8_anm1f)D<%LN0wV>fG6Appps3lRMD@V!X4hDTa!#qZzY9otL!jq>-9jU0z?n zg^E2-E2_#NtsM%L_IU!$xRe}WN8+?*#hnLol1;SeEhbFknj)Er=x%IAfmlqdKrlhQ zG2)KHD3-6~EbV0&_c3`2i)_ntBg-sz%H${Bmc=ElCVI~UAi#werB zY6d51K1Q4%z2U((t zdOyKzi^43;$~N9}!v!P+I#8{>N^1EdZ!o`r&djSLs?6eot-n2j{p zy+p7F;pd(nH-g;LgKW!VsiwfnoYO{2;fYZC5?PMu_eOiYda$=vJp_0N@L8y%Pz%*s z^>M&EfjRKeI9yhNLuo z|38`IO1fjtYLtyBrtV~66a#BFK3yxz*&n4wL?JkDYOZl;7;f z<5sF%XqH65a6GfDU~^*$PJPcFwGic)F>j&^1dg+_!;ceMZ?lu?A41zJpqppO-gQ#KPA ziON(r#)&7X3_DjT<5-^Kou)Y^Brlrbf|T{?0wj$sPR6jC68n$}7%8VAYiiDAFo>pI zglLU1M4qWB3MN+DwQQZUgTSF?L=O|2u(VmAy#I3;%I!HT!AUHNaRJDqAR&X%H?82lWGoeIOcAqg>CpVU%ayB@GMFHxqwsweQ7Asppxqtvn2m3IV!Y*Ruj3xuM z1^rk(DT44TPtXU)rxM{7MPFA`4E&f=r%ez8`I&BVksT5_=L)@qrL(dV_pnQ0^g9M! z&e0+43ZzKn2CTg2{VDg@prefJLi_X36Q|({rlk^*V9v7!3tDu& z1{>5&ayJ_BOPP#oUG%=k!-*_rvMA6zT55_?rwEfiNkY+_((v$T&}ScT1>K%t|B&53 z*hCd}W*hw`l9%vOv$RB;$WPQ#@g!_6I@)DMBVVEjmG&<6s(4Xqc`Pwa>s;6YLmMhv z6cjdXLF1(as3O7L6(jeO*!n+=Wv)349_Tg-vES zJJ7MZMvP4%gUYQ$5iXC?&$3p^dB;pKn6;XXrxWu@VGNp+H?LFdW4P4R7} z6$)CKR~?Vd6k{-NkkA$s%vCEaw?n~6Y;R0t;^=I!Sa||r3XPR$M*s}R5qLy@se=-0v=q|kA75YEnQ8;@LqSd2z@A+~Wi2kGYVdaz>*+Xp)Zi$raP>Q<9Uc7w9{ zW36n<*{;wKp@`JUNd`uaqoK-5EY*^6O`F-1;GKs;Y|YBB->E?kXQoySKs7;;`L%ra zrKOz}khjlZ|BO_v+A>+I#-|Xs8jmjb*-MMv*#X?qU%%S>4AjEkyxN=JQ=V&Kl*R~c z{6=#G9lDU1U(9_K7R&sKc@~S+YOScKtejU-Sy4I9?rXLzgIa?3AAdH=jgdITDX9Zm!3# z9UBsYNE9%SSMj@t6^DJqDX`(vO*l=SKN?D-hLhsF*vm}4Q3KuWgW-IK&rg;#J7UDo zMiJ?o)Y3@{qx1_FT^u6g`;_Qu}+R@DYZCOv8v*nnouAX409iiE&e& z3|!=M5fUq5oM+vR;nwcX)tzgABYmfBUH4j|`$k*Wb*`c;DY6)COh#!=NQgC#XgogZ z^buaYM(xe6ReKM()ZT6f5+1nQuT^`m1g=XOndf@qfg(GzfOkG!t7adoRgXX|{b{Z0 zd$LwlfV;(6uipK0S#ECxpKmYlvT+`ZB61OJd`POFcCg}W9EWP_z%Rj0hS*h$oTu;# zLN(F7@F}{ZjU|@KI@Ow6BboPU$?{}eF z&FMYl9dLV^kQZFohwO1V0?UqJ^6^CKt>JJKWl9b2 zrxjlGF|>UeIsT#KPORFpeOVTq(U2^$IL)EW%E{eRxfu)UcwJ%J)x)N1LC8)!zrr{;o^=hwUboB6&FU+d`yn= z3So%9-DwZl+Z(6a9RAH@4ng(PSvu|&V?Vvlqv0t5cSFC{q3tb(p2L0t!rq}HVM2-L$+k3dExa3p6bKC7@RR>WIEj(PSuET~L>|V!IiAqIEtO7_r4WO)%B z2BY&fIEqD61dL0hTa@%naE#9h@D>@IOE8%`ZJ0vPe884u0=$Vf?>D#5xLwM}1Pyk( zpxVHJ=DhTkY(-+hsqtdLCx}BQdA95f8d35KgHcQm1aZ&=lLbM?pxrazYEg=D&C*?x z6~{pXgbd-!MDs$~ir+op!Qtt^Rwu^mn^Aiku_F>o9&5pR@>VSRAFdQ)2i9GWEJmBv z2m<;|;u@%@VO{~IZ$RxsM};~o)#$9$FGAz66q#Zz`=_6hEimIv#P_hdkC#7C-l^dY zx;q^ZMdkhWUYv_WW2?8dlyC0??g-R6G{`}%aVGpzM!eQglZ=;v@8}|CMzZv$fM2M} zypP}rl1H!-I4iX%9Ro%ki_IJr3GY z(3&2Ww531P$~ffT4%&Xu_B|?ThksnFz5w%%uc2%Kc7y)cW5(-vZn$)IsP6 zTmgmL_j&!S*%ghZS}`=oreh(;Ac*F{K)~1D(biF0gI$YSp9vLeZOvdZqxDUhltmTM zDo5*7-^#^8!L2sQ-~o=m;Kv)*I(c{IJp(Lb=k z*3!|kyfL#}$JreL!okS=9{;e{3Cm>H?-<&&!KS(EcauoCm2--DG^eCnn6lc0ES2!vhmJPVFVOSfRU$GK5@@ zk_b3H&Kk&Q8N#_NUvf8k55t%tl78~|eOpF2nshT9g$FxYZ1g9?gwc!J(@z~D8E0gY z;ofW?-MV?h>RkMg*)PROULQ;{`pDltKfDbwI*sFuWulw!>G!ft&CCJU+#^5g@!;@i z;XR*qJ)DfE`uyx>%W~q^($4MKpe4D$i zs=b{5oF4yx+sAB(nl76SzB%6^vE&F6lkFJzDC&T7>Hfwz$l)Ef2i!!s&>_ct6w#88 z(Si~t4uPw>R&&3fe* z_B{KIukN+L-Jx#9NZ8Q4t>fC4U#7U>5+j;q50Ia^z^QL~dnau(P9mxoG}W=@I=_==5f zR3Gm{ePQdWr7}UXM0b}Ewl0jWx7Ld84h8-k^2socY$j-1XX|9xqjaxRY@Ld&LyUUt zeb{v^WDSLJg*-7R9*68-Ks^g}5Q?Nq$>i6I*?;!HpcaRIpVA0N@zri5I?4cstb~9L zzjusPVbc&=GVu`Y4?_DkpLbOWarJ&l#Z?&9$gad`UkF&y@ZPtbGjVMC*s;s3EDyp~ z40zoA-WHlyfE!c-kcS*Yu>^WP6d%|3bDuUp>H?kchX`+D#vnDvWRKdpqrl%tv{iX4 zI6m3csnMST?QSUIe;Cl&U$0*ABKmfJLZ9w=j2U6h0zLz^9*X#00N)wFcfWvs9&p>C z7DB%Q>N?on3w0gz1HO@AYMTdq0|RzIf3~9|$eW0G`>%V*vo09inV1TOXJ$G&gG01e zCzZs3$vv)47t)Ku6&X-_VQ+QNPX{7#<`pY-w0HP;mMQGl!|nknPv>~1o4WdtN%+Ts z)BCc|KtDKtk$M=a+Ji7b?M1)y2;g?8{e-Jtq^^L{`?5BaVfbm4(NEaP?e?wN-0yc| zNPC=sksoY>7=yrViqAOohrRXcftp3?oq#t2-Us;1sf*MX>K3WllNYH&OBSgI7A{hS zSXSSsgl+rOn|$Be@t>*cw>5v@4WHQc`76JE+xP$d=|B9jV{yGT(>b#Ij*$a>e@_2s zY~9soyyMMZy!ey%{NVa~k3Ij>pO39@bSssXCBKS%JA0$^oqTl0}?|V<2ciVz{ zCf?`%=(U&sTjGare8F?d#?H12&q*!%__444;fW{z`rzxn*7LcGK0b5R6(9KY+rIPp z-+p4@_gla2`D60w+4@h2Z`gjWRQej#{?yJLUw+4RSHJqnN58n?-lacy>(Af#{O7Iv z7eBxJKRQ?SEg9K8wmJQqJHC6``#4?m%OcVJUb9s5t!$E z?B>6G>BH~3^V;BlUjFB^zrFs)KUn#v)gP!jeA<@r>FHDZyEhO0Wb*z+ueAUAz7PNU z-S7GGt)F&ojSg1Lynf=y8BbTgeeEZk-tyh;kM;icoL66Y?;{`i%B}zUm8mxbE?#r# zX_x)6=ApB`c-{wl+WuDeyZ95s-}}rx@4fArkKX^jm3!yMBimg=H>`h0%Tr6gzOdmB z8;@P`;NB-c^0~Xd_LHk#{`l#M)t8TYvTwY)?F$>fzv#Jh#7T=^x1x1*@1Xr+e>{5m z?lbEbST}UHue@-p;|)`LcP6lLrunpn%G%2^6QMV58FHP!e4VYX>g*-qOW!mz;2GT> znVf#B>K@tF(ztO=wdJf+-jYp@U*y}=w`%?C+fF{CqGof@Ih=m;j@UV;FPzuY)wImp ze@S51*q+qj^_Cf!}(fIDmPZF`Abhd@7%Q={ocSOW4ls&I&9e5 z+AoV4l^rTew`@Wk_Z_Y$ip!+Y!U>eiqmZSS%R8Zu`0CXauR}R(uUD^Ix=6jH8RP!* z>(z%^Foti%7#?5c5XPjqx{$_bT)IW@5-6HJcdt|u(U!rO9bGl;f&(ADW1yvQHy zx4VamRwL-_x7-56w@_h11&hqIAR`aL@|km5z9Rw_0E5~UJoy!VF^RLgw8TTZ$57Lo zmw4zNBHDU@p{ss(0bL+rK=UdaT*MSw(jbV9G*K7CxS51^3$vwQbOw7h!GW1DY`DR; z3VDj17JT_c1s{39!lAaszX@jzw4G-xOE+I(QwS}}X@m1I8iZ3U zjL`{wofsq$`dtM2BwsigouR{gp-2d8muZDo)%axdWMowfW{`nPgyKE8(F~@@IB9Bd ze?l2cI7!1PP?mv4%ye#=C##kzn>AzO4Bu6zE%nM9(U71JN2|MKrD2sZ7S?4Avte#Y z>ms49)$3M?)m^rZHBp<`nTjQi%bkrGtX4M5Ko0oaUOL5s_Y8rPals<~=H;|N*0ih%AGyU75W}NeN#Hi} z80Di&a0wVr3)*J7#>dg4wb|ss^0_S_@3DoBuKs=p&V!n>IA&_JjH2JlK^S&29>=8| z1>tlIx$GmaDWoNA{Nut};CD)zEhFxL>o{qX<1_jbH?bhnySowT%K3;;+o~?a{`pFi zo;J#PN_TUa^f_V6=CtBmhbeB%!YMh2JmUH8KXrl*Dp%(1!(BoQ} zq}`63no1Evfecz5P;jLT%VO7S1YE<(FKRAv15uVvM`oM3wl$Y;IrGW|8tp()Qcz~- zce{q1Czx@V*O(lbNew1&-n2~KH;~P_yTC6+HR5snQ^ardlODxEV-5P6YENP|`&X+I zgR$h~)C?wkxxom=;~Ijupk%QBlKJGMv(zUe`i{DsS?)2*NswjS#Kq;lB+g`;^F<+J z2^4vh=JU~U;Kc>@W-*3lv~gHhTM}j_o-jwo4^qaLe&%^^K3}7WK?c;e!AS;W%CzQE zFquHd1IK~$tVS=8DCXvq9?S!$sGvn~6B9|57K<@PHf-Tyt2r(mp)ujUpaMxobDl-F zL{h*3W301a!!L|joaFu*HVh}QTbT~=@`eIglzZgPCtlN_+d%1JJZyu-WuHlG8lc@$ zg%`IL36!UJr7-k%CXsidnQ+L|TSL#`ToGX?dJkx7b2 z&BN<9y06MgMCGXObmNMxtX#S1BX^7CVup)6_EAkowMx$54*6K~OkoU}c(8W1Qxjh7 z0}_(K0yN)rQz9@1M~)xjOeCaGmOFk#X2S6k^cE zpkP-4ZO+`u{=kwyR_kz8UU`AW@>HB?#_WguOx$cT<_hCCVY{*cWHkQM%(9*EiDzXnG9+8{@cmM@eO{Qi=AvGaQuG zgH&p@b4qz6`QFGx<{%9yjYzqPE8w_HZ2D`XRUQ8fuwo4nqa5f!Bb*Pz;r*WGWJPa?HaC(zf$f>nX*vRDe zmKqQ;!Bp)3_c}9lJ2!fJ)Cz2aUK^JB+fW#DbAgH@g%&+B*Vs`Wu3b`)M(V4@%_7h)|vG7?k(HW52aXB$d!K_S#c$6)>X_z~l(}!iLrv_8Kva(T)Lx z;~{M4!hNZGFo}dNk^BtodYvn`ieM70d>zi!J&8>_j{G zY5P^l76uoY5+hYI$7?Ui^Ln?Xyg*!$iJPdX53D(kD`VzxA9bj#1m@sD&3k+voNPHR z6E-d$pNXq~REY6-$q3UL!m@OZB|bP%K8|EeQoCMO4Bt1;^R2~Va{G+1C`Qx0#T>@` zhOIz)>wVk~1bZxde12ML)U!g_9;}f`FDZsDiTDr}O>1gqjKwO8u_GVQL!RA08_74K zTQU=yM2SH`$EC=Gtl`~2+=e42^o^9{bzI5BJG123KFMZkhM8?VzadOA(R{j0SVSq6 zK#EsM!X{51V#;A1O>^jYtq+rt=6v@>N8kp_AhLG^PfJJ^9vEt4Y4%4!PlKzq;_eDF z>ircq6m(b^qxq1^63U%)3uN5!$R1zd767 z>7&)5<6qDHKXBPLO#`97K}UpW6xq5_-nA|DylDZweRZlnQdX60s=!|C!nu$~yi^CBZIXYVv;P8fg zQU@con6dcMwVBE=a)dt3f&n-=UC{34nDoOCEYtb#O)i}WTRt=u714tp*Thyvf^k_qT`s#4u;cVG)i>0S?A0J zR2yMnYCy@`?jq zLojr$F+3*uvH|5+1?ZAMOmQSL@@9Yi()ax1NeBleGp~@+8AOcojgry(rQ;zypF`!J z?s&)TK?UhXJbq%5x5_CvA1YrGYC{v=j;5U(%#3z!vIWNhaCM5JVzU+M*@6Km?dZ!b z(;a6fy@1y^i{7NITXo7sFRV-=Sk%92-iS+^aE)soah@!+yd-9uAq&vrM%SjM%X5~n zSc%J?bWU7NOY3L0nYD5qjjs>HWwd42(EeGTF%Kb+Q+FEjn#=@2p723_D@dRc%c93g zrV=*(@rl*7_Me+R(tUH1{GW)~@~g-HkeuJe?*Ctm*?)4DW#{LNSKgFxqP2A)^FO`@ z=(PQFYXIrKIX=0LH8!`Kold_KTXxVrqG|n(wzZoJn%V#@w9sV(#KGf|Rv(^8Y~9?| zrnoU>V_tgkal724_30FhSup*-N%sHoK98YGWOBKyt*g*w54;ni;R(9IIDX4d+u;Sh z@GiO}Ar`@QBXkOQ83yy-8AWHB@myZp z^1=qEV7(msMEu8TZi@JgS*>V&o#P+#MBMGh!7+a45x0??;C`j@*3AiBP}LXt@qZ~y z(tH2qFqy5J!(_-jGMl-ba`B2MsrXhC+gj{=D<5`xAo8_@ogb7m>^S?!^z=Hg{j79` z_(!^Lj{Wkn`rj=j|DaI)lRFL_|7SZ6|8M43tv10*F81h5%MHQh)@>y(&ys3$s1lUc&w^r<1krCfM4WhA!)q;(@dl#_eI3)fU>J7x>H@ZQ!4Z5F_0 zx+20By46iSu~JBzGC0&vb4~PWFa8Rj`|74~ZppaPHmbC15Fsm+uX_q>o<)Q~@+~?8 zMFb88Jym9m1V{>CZLW-bOvM$k(*YE_u{w zsA7vmYCgc5Cgk~OT1%TDx%Iw~o!;&uNBo!}5lB&kCHXX0`S`{wE82Y4YqN)!MKpVb zUO|v3GH5Sg5rI?Td=s9ZP!p*ccgBwg?f5;e+=CZ5tzITl&FRJISSpGUs@E2Ode+r# zok#@2`#L+P!|@X-g#ttuGSc?+((y#ORc9&{nKFOq9498G*eRBBf^lq@3kaoivN=I? z<~co9&QiGoiN|dwDQ+Q@RzLCJ9_4x&_1Mn)8ngb|@>!TGvDcc1&r9)}d7YSL)wFmL zNYB=&H7J^C{x^Uv()`{PJ&Dw$KYw*zvtApNi?UL@bHo&9ucDkIi`z^eZ?&TO0?VH^ zp_$2N@coF}Gk|+^f^-QHZyqw{P?3_s}`*3;xOt=RfTybhP&?;Y`%?0U$hrS@A6CNL88dq+ncxKhT3%We<{^2Oc^uEL>ZJY05@ zr6+u4Z@qORuNz|R8h5D{o%JKbn!iakY zY$C5w;-$4` zse{?`npZZZ7}dH_QoxFyvDA}vZ9z=qD&1#hZaKohwj^nn-n9?!(Lyd!*sG3>ElFI4 z-Gr$;aRB6 zgurfptTFNy0Rew_yjw&nlAcD3)ZZW6>>U~%#v|wELC|<8i)%$O&$ug=)BwUp@090` z0E~~*Q3lX4{*ehS`t1QyIKjI7yq@(Nd-uLooZoW6hHaOK-~N67u;1zRuWGZ=%@7{@ zuuH+m%z!2wkrdp_P$}w#O3xTsN2j(q(a_a6PG z-G1Nw-}&wX-~0CmzyEzH$^DZ@AA9@>_mkeIAK89L?Tn@8h_{?=5#bhb&NY^U=Ui+N4b@fk4OLYNpbbie z{=$a&;-ZF;hHVXjhD#bYHv}7YfRbuhB<^bXO2d5(_cwf};kylwHaynwa>FYPziIew z!(SR+ZI}hmeyD>`hZ`!zoef`Z_(H?o4G%O}#Jvpptk-vFxE0XU(&L!*Z|XsO6hb-?rQb z^&QK1p}q&7K4Gzl_gj8yS!{XUQX&3mc>#YfTbeA(t&1%^){{iEl>{>>(!Q!b!dYdz21_tzRCJgOU9Z-=%%f^tuxj=*2}DKw(hk?S}wP~)%q#R+pKT5e%i9n z`cCV=Sl?y+ti@@iFuJVov%cSYm36=M1J*BD-ds^3{#Nn#ienXrDk{alR9sQd{#V0F1S#h+Yy5a$FU0Lz|imNIfsW`*(r-~Z!O2zXPFI2o(@lwT~@%Ku_ zU+`zER4ROx_49XBF1Ng=a<%2_m5VG7R$9c7%7-eQme9PF*rxJr@mKL{@iXxY@s#+b z_)qa0@mukX_=EVpcvc)0-xT+YUx{yt7sN~APvTYaiZ~{IBpw$pi%*J2#E-=XEl-Qz ziRZ;%#2>}?#b?A_;%nlw;*hvU+$}yKZWUhTClxKDIg zz9;@&JSfg+T-bO;2%;$iV0;zjX8afkT3_^EhQJSKi3{wAIfpA)x> z+r;O^>B?8dzlod0pT(Wxu((BhNqkv+QG7uhY&<9$GmRPTp75!JY}F#T3&-Uj9SRgb zydY~r{la0GP!GZsgwEL5)Ad}`q2Rj_lQ`B8#X$?Kf#8Mv1P*5Dw@eqb;KW7`H{e7D z#`bKJw%6md0q$c(Um}HV*1B7$WYHSNaNr?_!Hu19BS!{T9~;elV;CVIo_1x?`-_;% zPbBl3vcbn{y+4xa9(Wm>{XeJ!_PyfiQ+t+0hziyZK%;D4NI=?PYH?M%5 zMyQoguZL2gmO(9tIu{Dp;PI#Xi^5w6MVBbieM^g=UI%q1)LBr@{V()h0i{CigBpa2 zL+yZyKutgepteG7hw6q}3uS{^4>bhkgGxbVp=O}=Ld`;52?Zy_3cx0)7R^jJ{aXs0 z0<{F{45&(|YN&-!^-$-WbN1PE$r^=c$Btl-?rfqs*}IQrSDyyGEJdurj{-%di@({N z4n~SQ&0ginL{{lX{}98ox?ZSCPAlVEvuC9^3Rh89fFAB`G}1->j}7({d=yUj3;|&U;L{PM+mYB_aAp`DP!x zXXU)Ym6D89?kpNSs*ps=v*$oj9=hTL?UBp6Jqpv6Y_IaT?on`cd&_e6to(GM0<#b* z)GWsce)SL0VqtsaM+NEtyH_nZ-3g(GkD_vx&Ga)%Msn|LC<^O!Cu$E7s@sD#as7$f zgCn{pY7Y^r+oSS*`i+Botdq$i#`$uug>?>S9 zkUKSEvE?LjmSurhZmAOISX=xzyzBP5 zC0!HCZs=MY{@un$>)gF>yzb4tkJjDKbv)RF?I*bR32MiddREWT9k9@^eRPp}2>4m3 zC!qfr)Ll@d7mqDc&o!xAfxq8C$3=7NqBG$rrWuk6xN|%1#QQMryaIX*=T3N1hTNxn zunyB*SWvl3^?G`OH0dIzf5K6@*Pr)ah_XELO)l!$CDX!iL0@NgIaw~<{nOw#a^Glc zb)hYz^qjpR%^u02Z{gF6)NZK1K7}~aJ_Y7Ms7fduH;6di4rPw>hk&~d%8B^bd7FN@ zNL>$Q=H2%U?)CzH28tpycg~Q`Wza2uYl^WH*QZ@4!>Y@_{x`To5b6L_CL8JLDM%g3 zb>O$SKMd+hsP{a(NWBnhClu*#gL=rMw!u6NrPIe=TBJ5Z^+J*Uolv)!R2R%1D4nkR zuVZ1Ix)RC@wFQdI-5~z^tH+Lg7a5()Ak3>u;7G>Z?|$UkFZ{>#>$`5g<%j3K;1wPHl$T2lY*u?}ds(?S~pZr%wGc z@GXF+LXlZlgfFLv*GTKr%trjj=diEa)!jb~UgFo?*72oekXwY;-6UDM-pEh-$DkI! zu1>uh>Q7Le2=fTweP`FHNhpFJ(G=NwmPvC1V7sOtTVAJ5 zoPg5ZC1{2N5@4v^LoPFxmudPzb21wy&$jG?U|v`{ok3~ zzYXpY?)RFz_iOMAZHP~}|In&Djokkf;_PvQ`>Qqg2~QjNjh|xra6PY}d*wZL58p9w z>*(z2UbTA7+I69^Fb->oJi^|G6FB`S7nk|!)LVw@)Z3t{J$0%Z<^ibZymjgl!0%sJ zr)~iLFyM!w$gC^Ems7-Rq>8}aAPVPChayWb67;@92Q@ug&tTZGr$Bw4!N$WQtp z)HP5l!aEK1Z%{vic?dF|NB9XgXo~dP0*IF>sbU5_;hGKR1oWv<$+rve&6@tRo9oou zPeAEzl!A9`lXBm%rA}?zTBnj(S7!bf;FgZNb3CsnT+J?NZj9&gZ%gL&x;-;Ay1&lk z{wr{gaO>eWvUjQmE5fKp;r@zC^E7h*Pk!WUgZuB%+-JNovqNvn?s#noXSC?FrYGbP zBQCr%j6=3`^AAolhQrY)?)VYyV^gtsB-4(I5#`bv&JD#|Jp=7+OlTj(-SVxm)oWL` z?q1#9I*B=}-8hgmgR_R2P%BnU@M=OdKF+Hf+5_bJJLIu3Y^%yf=uPrSHW|V^Sl>Uz z@tx#)E6!t0L}%a$e2-PBz1Qr*9mfaN-eYuozI&^15Mhq*|L=6$)z#{u>*`brVj%oV zuO0^!4|A!)u>$oY?0$#aJx5F(kB;|W{tTymL$Uv_stI**W?3TODG<2X_2o>(dVP$~$Jx{@kff z-yeSVqhFl0$@#$_4p%+++^qAZH$44qYwI1(+duk~J&u{*ILAL#rylqu+A^pYpjN@$ z1@-t%$UA`d+>Cns#ya(Sz;{BCSyzNFr-;```+{aR;_K$HuiMq#SHMgBy4yOwlnio< z@Vc8MOV=CuNxuo|?NBWU?-Qe9!t6V7fhzYqEY zw@SXN06(eee|QLe$`eq!8*{<2+t6?PY~DX}@e=KmcNFs4?vmyeU&-UY^VPimc=E{o z9+Ug0X}AC&+3(cgOP{Y(KMVH*Uxw0fsG@NV>3C7gR2oS7lj{(=1p}0n7saavEuz zR}JO`^IT-zHV1{ z|90>azwWkNKyE-)kMeyUABK@n+ zVa!s4H_V_G{xPp7^EVB&lR-Pw z|86Y~34h#r>voF}NvK$b5MzKo)HZ`qD%4si8&ngN0(J03^jlx-T?-X}+6tA0nt?h1 zH5T8+w{6o#d(y9$_Zb9nyFtk-==jEh-0=r_f5B8XhE3qKI|;|;X}b<>!foQ$<9S_~ zuT+q)qGz_jkxSaJ<4$Q+=HlSZO3g~UzGs+r+;(-+e&K?nOmvMk9qm8PxiFp0c)X7; zbHD?=A>LU`Qx#nl9Qit)wXC1c^WODT#eY?Ld=DKb|e z;(3o#y_(h{)toveD z4(ajYUTE*A$60{!kNW6NdsiN-zYwJRLU$IsI()cMB`;7X;5v|;NFBG?HR5-BJwV-&z?*_jJhG(67f4q0f_RnC1plUO=B zJ+3W5ElJIJ2p-vreX%Z&W297QiRN^br{TG}a@L4c+B@tRk&1IKMGRti?mXl0j*N`@ z0>p?Y!h=!92&c#GFdc;368rm+iwkI5Mv!P6){A@rWBwi;@)c%VLimwcIjZ~I`III? zF6F$q)P){<%Q-?+=csSU?Z69V!6AE}E1w&PhT`k$cY9pU612V%y9ZY|;^pF?w?7EO zKt4-)1NK7y5rTq_Lgn=Y5$1llnU4q}`&~nL=NF#s_v${*`IG2e8<97u(lTUnwu?s& zaY2*ILD{Cj)`)j#sL$@$RLWM4rbkd=rsvy-SeSQHkPShHC-3JRCg{fNGRATwDI+eQ z3*N;wIwRhEdN{mJoF>V6(NGbLdT`m1lQT)*5It>}V{+PuQC6K?ml!R(30)Y^62-4- zbBJ>?BtZ*k4!gzO?=BR;81fZWfQ5Wq0s=@xL*(b!{dj26XC}D&3nkFojD?uO7-wnp z;Bz4p3Rxk-Sgo@iJXf7>Q5fU1`~6$IBPOi5v0>IN_DwQV6*{uTJ38bILJUP>utXRswo2hiG(7Ijw_5DOz*fFYb!Z4X!1cO=3Fv&e{ecmCKF`Y# z`DBw+$h-wuf4+T(AnYhFOnnVkl>Qpt*cHgJ7IYdkY+-Sb4w2Bp4uQsuQw^>WymFiK zt%i4PMcB+pLzZ&-WCvz+B%i4WhuESj!i?4nxjhibM;CU`4;DrNV>lMc!$>O$*X&`D$)8s1pvF&_N@RPxlC6(+P!96@6Nc?G>frg^z} z!)1|%-iVPf>e+<6ny(g^wiPW2T-<_3FAe2_Nt~tnjU6jSQ%LP2&U}+s7&{ud5V_eK zFxI)!T$e+Wax7ehsPCg_ZQ~GJX+7Xh+O+{ zUZR1apSv#Pem;NX$<9HC&mr@&xg9KI3fhNQhU7J5hq7#iwSJz>G?>T}$S>?!bBkeM z8F8Z-52B#r`L^;N=E~L7888U($WECol&B2yDU)X!9&)ap!vWuF?k<@*Wb;}uY>+5a z>IH=*hc%ZwNziK&8fT#C&*c6zCNWU!x8vl7e4Ejbm`0JridxC%p>8xsc3JkVt8D9n4jGc-s3;PO z_CyAJ?f{L-GF`@dvN)8H;M+uz3a`+>uM7`G8;$32b*$D4E#neLgwJ0zkTHfU;HE4~ z$x!U;E#9GiPNUE_VN|KLJhp~GgQF78}#5B|_par?d zocE0+>W+`NF3go|Ns$Mqf`O4yPkz9WBl&`ZhM58bnqDF|NunOBqhKH)`2)CDEYG?H zr4thwUQ_ULXs16oh@l-9l@hi<*298KG&h6OJ>W7lnS(|*bRqQay|LLr_nPXMLf`2~ z+4q(ceVTxr4|Ekd!vu@&(YaVkPMnkwg(uaA+yj_{DH1m7aSsm+!^1R2E$bvk z!5GJu^E0~Z_8d{}M}OCC@~!MYSk+z&mO%EyYz6!{Uz~9zz=vnU?t!H;5@((U=`q700j;t9R#!h zeiqOQ_%*-^z@GqC0=@`153ur!um{)-C=?;=fEK_2pcQbE@HY65@PHp8Jm9T>6@W(p zD*>MXoCo+jz$(CFfb#(t-;Q(uTmg6z;3~ixz<$65fLj3<0ww@!0p9_*2=HG4>j1w9 zxESyOz*7K!3b+LDCBRbw7u|t)2W$lF0^A7L4LAz83UC)-1n{kZc!gJ74LA;XE8tGR zuL4d0J`5NGdjA6oL_P&<09*`s0pKZsn*o;qP6D0^cqQOzfS&+79q7oY-Y1+)WJ0PX;+1bippJitSMRe(PLoDcXcU^QUfT?iN8YQP%60N?_^ zJ%9@VuLTr4g!nR`1@HktE8x!nD*#^wtOPvsD{u#JEnpSk7C@0gJppI|d>fz@@FRc~ zfQJDq0q+Bx2lz9U|+Grx*-02~B74KM+CI^eZ{X8_&_ zcqZUa0M7#a8{q2zmw!#|T?*I_*a4UT>;$|9unX{;fZc$<2V4bs+7Y#PHQ)t+YXG+c zt_6Gxpx7_O#{eyWcaZrY(v8f3zXY@bTD}haVIfWhv;Z~%S^=Ga6@VeYO29V*&I7y# zunO=t!1;jR1gr*p0#F=5{SRmXoc|5T0bB`K2{-^a4{#TtcnIwYpat+IKr7(=fE9qx z0#*VpxflF^>j0|&gMjk^-vY=dzy;bATsnx?-bFj~I61N|=uhD_yj{^?W&+O?MsQi+ zPT}bs&#di_#iMRqe&Lemc_#37J>gf&XYGNN_*vlBY%YB3{yO0HLv?h3pvwV2pZ@`V z2Fjo9>>{`2T5}{w@95$Xc03f0x(n?2LvFt<=o&H{*y-pD4&e<8JmL|Lh4;8RUGNRb zegJYG8bDm(yAk^J+w0ZJrh4^-_Ih>y`St28&GqWDfDg6QtJk+u%^<`P@WsJ13w0gT zeNf%t3qZ|4&34wSHsG>=Es%G`d7uN%wAHHzK(B|Mm+JHo_ebbB*d#*)2mxeXT@P1D0Zp-H?0Jx7q~R+!QOHr!XFM=ZZOiEEP- zj^W%}Fqx?o-S?#6H9{@a$BT9fI6S_gamws*pbC7ez3It!<)oE!O;p`uoH?|oZcaEf zd>bkDR-CYf*XqDTDv2F2CoEnD;^@x)BylkM^3)ogq_cgtV%P7Cp zMVYJ6gqMc06G0h3NtsGvOHDMGXVqUG((WB9^FkK8D1D_d8ZdZNL<%3HcWHEi%q{VH zvCWDB@3CVRoPxD1tiUnalj}M~PZf7)|1~=DZXQU6d2e%s|qZ(s-qR+u%+tu3UPzA0s8-_kQVFC1?>Epbzi0U zV5LM=cU5f^+oitJwwi^om&nbT3gk{ zmUvZKTn1Sn(g$&iR>QdQjO#7pQ#c~aj=mnS=d4MwTbgUz0Q=6mOzhJbPHhL`g8M5D zSBkqR01sAHK?_;+BX>jaerwg8R`Crh*%oJ87E@#_t(vsFrRp6PaV5#nTxZwn7Q@Te zSr*S)#rw&M^zR!FTZOo}vg*cpmU}C`IUC2ssg{MWt6FC1YlyM;^5Muwi>S3!S1oU{ zv@Jx3Tn17navCnFQpFp{bBIfJ?_^8m*%jx)8x}eba0Kli#o5A&lNMS|t$N+Om5^g0 zN!n=vLmDhR1#+6ITp}RlE$Rdu7{13M9?;@y5pk^Ce3M1ou46cQNA9qQqZ-DxqC<#_ zs~)zBiK_doB3pGW-9~gSv|qARJ!la>WBtdhRrgrMlN_zbEazX`hGCqg>J|&~SR;C5 zma129XOk7!?^vsTXcZr?xEM)MS@lumUcL{5!df9Ldj$r*OQ0vEf5ameDU55;k$^_L zvkZQF3H%uvuHrJ^5II>T4n4j|C6|RDnxfGOup62~L|YH_Ku@ho`pQKQp+kn0f#=A) z73vqDk>91gJS_{H{uyMloc$oY413D}&GruImU1*gNcZlWgZ2y1mR?Y+s(_V?bN;lE zKPiWA2d$Q9WHy9hDMfv6GSB8d`gFkb<8%N!67bvn?*paJuo7UH==c4>b{C+Fdb51FOf7zKuKISj3clTR} zzo;HuG|Tuai{zwO&W%NQ$$xhdUh+R&2LEgkUWSY2Y4uO~U$!$v_244O-(7}(s0=<* zgqL#mmcbtY{;3VMDh<4~YMJBVP!Ye%_@a7HYbPHEf78ZVbvK}tlM-C_L+*dI-7APE zEwc1>uN|RlNOTTcr`!BS6T<*%WdrT|S?lG-Q zyT`Pm^ftsPT(I0zTU)7<@{OT&o<#H-509G_$z^L z0iN!y((`vY|Lq6gas%HIvJ0huGU&1#kpFH6|JMMeTwAUl%Gzty856!JU!vTU^T)HW z|L&Z9h2@ezX8LN?J_Ej-{UJxK`g#MO9*!fxcbo7Oju(I@9zA|YPw`ystW|H!;blFW zg<1b(e8_NL3FIO0D}Y8hWWU-~t6pHj7uTmUoDYLf|D=40e^y5V8u4@0r%t%LiukE5 z*6Jy$Pxk^pV8R#Ir#m>_sXjdl{x_KTsXndmuT?L~;blBfeHt&oOaD`SdLQtQ=J;iN zQu(@ZpjLfP4zIGiRG;dfF`i#2!prt)A^KXjL1TL<{l5bEfV);b0(&wZQY?Qx@cJj^ z%l0x*gqL!P`jv|$|32_9*;K1;fiNjw@?Q^p9Qfskdx@9)w*$Z5Q>(^v{_%=S7x0SHuLo`^XumI{se0Tj++C}77Sg!Rr!uaB_GBTA?NJ-5 z%wU{H@dmvXFdxZ0L}u-iG&1DJjyv95t3C&e!5+)}1#rH-wd&$R8vCVO8@d#{)P_c2 zZ&#tcSt=q#Uk}Ygpwsa|gPVtmPX65vje2>l`tQ&i=o}W@-7M!%O1{=jo7PLd}(dtf^^~}4F^!UvE_v$OP{HN8k z<@j2_NAk^OfrS#DG>eYp`uR@qj}u;#tG7#sfX~-A3YRI$vz70wRjEBN@>6_PzrR-9 zYLas!%sYVJYr@|R^W(t3*Mz5bY2qrRs|o)s%r^r6e9m1tP9c9>_kmiK%8T@mY#%7k zHhmCzJ;yKc)Gn>Rx>nt6;xBHOBtNxF`e%%{y+wE#Ka^(=L4Hq8zQj|zq<>P*iPbre zJL=WbNETVv0?1{K%m-@KYY0txN#iL5qHl+~4)lK|5|o*~7pU$JmA8LA=nu?6zZ>+( zHMQ!`NHmmDKIOf~p&q%m^qkqt#C~0^iYpq8cDW2LK|Ixic2yyb%Ztq%hwQK{XOw;i zVJ}i>k7Z7PR{7ytbt6I0HNWm4f9?a$`LF0N5N&&|9%0%+;FRlY)$>tKD=*8@Qe^LL z(DYBrB-5hQQv5zrnnvOT*?qY*o&5@O$#ERXRfwp41~+Nan&qlG5zs(qx!Zw@e5_W* zys$A2m^K4k95hotQ^~Ifp!Cly^RP|;G}9iKgZ4~m8oNj3S^vy3l{skZ=b&vZL!&s& zmZs&}E-KIZXZ8;>X+#5!^D4DL`e&w58>D{*8m9%dLHm#vX)s6{SIQhW)CTFF!5-7f zv_VYUTG}4FM{SV)8DuhT{~WYKbI`Dako(Ls|3CKL2QJDg`yYSinHiXY2OPwR6xU}) zM+eg!{*+j{^MI&OR%k}%+6v+*lz$K~$+e9^)6r-bwa`?R0e_xt+2emyS^=eg(Hd+xdCp8NmK%(;Ge81T3& zC_8;0tykHZJxSpI&HaskmtRsM?h16*li6${XyWrhQcHK9To0gy?+lV?-1aJ;To0g) z=z~XNn;AUON4hISDnlH2C&8Of zyr;YROIE&FzI~q6ZMJV7OIPHZmqLA0c|W`(zIjY$y>A}VrPDW$rK59%*mCzgcx(6a z#e(Mm?=~vCvpbK@5iWqY1G2s9U7|AdTJNz4@|f1gIRevV2V{C4Stf<#bZipfX7=Qr z?k8Oz=LjsnKF$#sua9$tKG$l;cK6;N8RIDouxtVx+_`Bz=fNXh=)M0z>8FE!eXVEi zGksUv^_uVY&R?FlCHB%wrqg)pd1QTMI?azgk2k%@Lj?Aw@90HO=~Hh{RPXe?_1??& z-4i{Ld*s)j-aBBw;S}G?E~NKbPhaOn@2$67{@41*m*(}JN7i57AGrYHG348Gzmnzw z^`Q5hM^pb3vB%y2c*aZtkMX z<=q2%Bj~KJ^wNj*KNY?G^mHHjHiIrA{)is(X}>8EbT-y_$sYmwY#;Gy90>N{oFBCx&*PPj zjeRtJd!C-WS^e;S#`w+h>tp<8V+w0OZe_f5VZ1)Z?>@(yZe_v+d}>R*`%Sr>Cwd|S zvwZtJ_mtyPy?Y+-_|$KD9+@t;lN}x+us6MTzbP?2PxXo~)2ZL|JhB{_PW`6m@uv5x zcXxY>?G<0v^MBHB{*?9>`+BeaL*@h2-W=fhnuk(*+wbAM+`Uba-H&;A2fOpAy`A>( zs=D*2y)}AxHQjmC-e`|E7?|14t6w-rv^RpJ-}vBBdrJe4zLBn;zqw_@d4tNOQt zr0Lz$vNC6bH|Ch{bxjd?o=28Tp6sp5;5Kg`qB++$9 zFCNP;7KD`deYcAg@GgKi(MP();F(Uj&w9Z39-r+j6C!%@qc?fn+tClN8oXJM>8tKf zgSQ<#sY@mb>h`BAzGX7I3{s;#^^wjDUc?9RZB!@|A@?&Z-E{CgkIbWxT!u{W_I`x^ z2|nkQjgeDjzu74*Z~-Vg79 zZyw9Q16Vqm z`{jXmS9c!Mh30$ly*j*+oJI(z}_3nK}@V)Du z=6-45P41qK$)vg84)DxAcr^Dr1zwN27E4ESKk@WcmG!y;3Ig-|dwifX#jf`^aUu zK4+l4($~JkeI4lJ#bdhAo$D(NLHLj;cpr4l!I{nI&UM_^zUMt_!CL@cISl6|lj-RI zPdMLe?#A+?JJ)l;qc-D}j`4oRooklvXWY3?A)ojecdnUT3Yg-u$H--%ciAU?#+_?c zhM#lin#v&bGbb|n=CQIT`sOiR()!`8?uS>>4{v`zyp#R#F7(6G_?{E7GK}blC;8?v z-=&YHc^jFwciuNoqIy^Po)e`AZsmKPU#Pz6evikqAGHJY z??@inCGO|+v5znBJI!^=Cd6#x-q)gik-6Wa4GHdkhNmag%!wh|y(x zC4xTUQt$ev`ZN2WQ~gZ@{rWv>GusEG`kM=S&vO#0zdX>{eF2uQ9G~j%Jm>~s*^aWF zRDVKq?|pQtzY(Cby)UnFQT@e&KEF$jTrR4=63_>C(PjBmf1by?UVGP{ET?z<$@)|M z?T7rQd&sByOKkDJZ!E{B`dbY;JL~q+|41MDobE-J*4=-#wM(Oa$HYc-w*YwLFhEz1Q5P&vO_#-|cuXk&y%_L@lPRs=G7o+URQj%To*t;2KwK-$|d%B4%^8E zOLJgPxjeES)c$8HgQe%XOqcr9 zuY0_c3L2y6>2HnJ-+7pv8s()Iw^Af?wuj*%%RDwd_bP{1x<1xuEZv@dWd6G~nkT83 zo=hgK(L7I29`$3M$BUOj<@*`uVQl`<$9WjjrKq3$`Zy0`>3a2JvOU@SgVt!C$EyrX zm(G58eXP+~x<1xuOqZXrMq}yvSfjE0e#RP&mHl+je$C4+eXP+~I_i%+Pfwfmai4{y zqcxi6@k+;deS9vK@z(lICz7Ah8qM=~$z(R`)sM;LW9cYOe{I@_OqOnQzWtt^i%s;+E1+tiv~zv)MdIc6!|OG- z^r|bS7}C?@WpgI8wjY`2ee;-asC@fACKQ$)NO{E$L~G2lJni+5dtjB`A_fW$#N5W z$*pH|WttOu9yvcb{!iDtn$4|y>n+Q9%_AIGmM`BGJk^UXF?(I;MVIB!n$Yuj>)C5A z<=&I{>1#rH?%v;;uzSApTB}#T>#kQ?6M7z5Pq|#Z=2F2j{d6DkX-(*PWH|-Q&a@`< zJl^&k(Tgs}@73?R*Cf5ygmV0!z9y9Q-`*=<**?D3gscyN^8L*p{ukE-BIVc1kKN8g zyq~cqU^4rdKQP`oPvro!tmORU@uO#*$~@K&_VQydULR`$mftMjG8wOrH38%Gu_j== zKGp<`*UOK+^z36zz{=j~TTjOOUtSaR+0Q4cgXQxI8spsUbxhCta?=z0p#SInrQDzG z=tF+*{!)%l{pAJdKLJ?Y1CZMp^_M2UVClv#x*VVSO9^!P?Qicr1M;)Epo=}|)L*U! zo!W(W{OO=S<0F3W{!-3&HRA6_{9!%h^w(dWLVT(R?|ggtnY&(2Lq6vp-0M3cdiR&I zKE3%Hkl)0DZt9}TdXV4D1s&gi)~%e>KNW*M zwu>&yDMGj!boRLCBGS=3GQ__oBT#~Z*M)xZ(Z_XiK zDz_Y8wkPu&cLv__$!`v5gQX)q;`jC&S$|)CGYDyWkF#?8-s7xH|LNnb9KZKCE7NJ5 zErK0-w)5Ivjdxo4Z+ewXlJTBx~KaY^B-nU+7nF*50++; zir4jIJnCPzgU8P3uJc&=XirpkQ*h6_r)-aF2Y9o=OM$+sSzUUb6d{AkPfvgTn?EF2 zzB`uLm6qxAzuX_Ny6>YuV7xy11I8o&Er|%0hEN&pU3y9aD;>(mb^q?rf=_zSPsSkb z9?*+`!y;pv$p2%({e1mC(4<6Yq5xmUqJZ95#pwTyuk!5qruV#?@(cgA_n9r_mk4?a@V~PYqOgb6CFXm5<$nb7yyxXV#h!z{bC2iZ zb$e1e^6>@W(G_0Vhvdp6-fTdD4_;q-Av$~ho&Nu?;)VYZEFJQyU-Ad4w?U1;@^{0q zqF)~$@0+?gT^lp=XNs2z`d1X6?t=;b>{y5FNNqo*2|h(jw+FZ$yk~sysO{%n3YNdehwhQN(~;iiDA?S4z1RtQ1nA7)m z+}GR9m=F=I_NrJkKdEtMS!`Wd;2fZcL4H0 zj|TRR{~E&OKIEJN{Q&4}e2`t1>2m?}TF|%jkVEU`G*_@hw(youec@uzNe6E_t(OZx zf2WJy_nj|9VUNe}N!A!QjgU8;?C5zsbkd2Qp7R-*+C6=5xsIQ7hl76S?6NDpwVxzusdvc_ZXe>Ab+Rd zWr-j%z?*jtLHbP=R!=>6G}h7Yznq~Y*LiGAruwDdbs65BCsKa2UfBcQ+3q|xzozvH z{jLnPch!`xvKN5Nk$!o2q2HS!y;S%2=05{I{T59v@fY>x?+2fLr{*;AAM4FO1wQ=_ z4ZA1)RChk<-V8qd_Klgwu{XN-Y#yfrZ3KSP=3bHw@V4U^@aXq$1RuO4@aQ*j#2!2Z z*)!-@LDDQkkaPqfJ%xK)MM2UtTku_afa(8@@Lz)@4Zh!|7~kQMhwo=70yyxQ`04oU zd*UnjY#~0AashYBQ*aluzy(>VU}-U+7*G!A1PsF7=@@{By~EYmv!L~P`CzQ^hGKsA z1jgOWBB_82{ETMXx8SoO zof_$5y+%3)cnx3%t#NFS#2GZw1%wwP91e(mSR>gHruch-1<*PXZ#&*qqDFf0^@`6Z zzh9nTEbxm3ezCwW7Wl;izgXZG3;be%Uo7y81%9!B#{%8gCGuKicHscFmhtd}cd>@T zIwKEI0C)zlryaC-toIiLNe9Miq;kOd@%TIr!pAa#q_u#?`S=`ETo8*#aVgI<(DtX{ z`@ho>4=@Pf12=1=lYsMX7%@g8#R3umvj7VKc>qdJk4LZT*yTERzQfYUby}Xe)?vw0 zbXu+e<|6D)fHeQ#{qvNQ)|Iq&WM%UDM{CTuEPSUt0Kazr@8i^8{r<7ImjCi#SE zjY^od-U!n^17X^7BurbNglXH7u*QvPpP9k|ZcN|vO<~%PAROez!EQXjjcFf|_#tjg z`&ASUbz|BWqA-01k}&Nr5~llBglW%&Fx|f(Oy^OA>D~@u+S4IC)QxF>io(O(Saf5( z8yno1_LV5!2sbvm@klqOb8_OF+&I#WN4xROZal_~>3$K#r+qcTgqW)Rx|lc|E&dx+ON?bCan0P1Ai4}Gtd=TTFq<9-Hsz2gT` zy+U#XFL9hl1hsu?zr-8yV+Z;_d93S}SaR>%nOoSn;fW(lb^p)4+$YZyzbB?VJv6xw zm-Ch-Uf&yJVb(Wa=X=t1VMHQ%q_>CWE#E`yiJ9!5^^bn)*X5$|em2ep<-A<30WBRC z0t(Y}r;L35?~f-sB+K^Q@^8F5+;|6Yx%+*O8}9|)4>$mL4Nwg@0`Qrk*d%Xe>9>RVrXsbb^ ztUh+*ZazhfpZKhmFs}k60vautQvtREIE0G?*Y1mhH#WrXp@eAxhU)4xyUuvYafO^15%>VYD z(@1fEqPk$|WAL}11r6`v*ANGE;d6|SNIwWX(t8lzBLLxFT`$LPj4K`zC*OO!NWVuf zW-PPji@6z@OGIl<&axbR0N2IO$g$F|($AkyKQC|16PK*WL)^ZirMMX$V=BE2QeO3!^HSIk(ElVe?)w-`TVzhdc9{F43c+~kz|Mf12QhUNce)n<0F9XorR9gw3NmEaaDj>0E)ucb_%TyXX92=9DS-%o3NRFJ8PXL(E-~ zkzutiurA=n&0mqVctP&CtOX0lW#lZ&&COf3Z1LE8rj5IWF~%j`d*7_FS+~XAHg@H0 zv16A&<&`nf-r$`5uJVjB)fc<>MADA#_WI zY`9@SG>@1A$Oq8(LD6?4oB~_|Q2%ZQ+ykJvG}j_bb1ZM{{qEuYocJ_{>r0kLt_RPW zgI~v<#Tlc_zxl0s?1V8lkA29vl)KxQxx%>g7O(%@I2O3w>py3XijIlB^|tYG6Vm5r zEU+$Il$rJLBa4?TU6#EZzxKZ3(Utj+t>SX>GUjD0euSGMnv5$VMPn|x_@n8IvlfW- z!}KexOEVq|@aC>qn!6%9ds$AN)hh;C#L|UXi&o^M=VdKh8o(KI#f9lv_<8x;Mby|l zxc|I7(VTD0=a5ZqB)1T}+eL#h*B~y=T51)I3j!9dSekJ=@(-})XQS!lLJkXZQ|?Qd zH)ZC`duPs@oHS+fUEGRXI1zNSa#~|H5{S83t02sn57F6?0bJJ7JnN#dzlM-U=jG<5 z=dH*UCruLbGP82$dDBLY6b%#>-Ep#(W-LQf$;dNs}lKBzxIXG@aaWnb!1d z_HSO^;@pLBE6TA;=zO^MBl8%EWU?UB35M(vNeSnrFIX@y8_s^ac(--QvYf}zIppSv z^Q|Jxy=W=yIsY-yxB~z4thu)YaK6*9j64bFTk}?0txG9Y`qBm7iQVm9B$dcmuru5&iW!cu8#mGS~XMt)UedIjmEw$$7xwDfc5J8m1UDRK_Uyszg?z?#I>htXdvJ+gd zK$e@FRBl5z%WdaoS;{=Ly#>~sdCMNP=FqFVfGmAihOGG41<%8n;pL6F*^4n`uguEJ z?CRMlxo?N`Hdgv#jO7c^O4D;5LE}tEpT>N!oAZd-nUCdWWuWF{r-cKJV*V<-kF1ra zJ}9M}1Ct@Apjw5ClWg=-%T_GP^vV&#W~R5Mi_>%SOx?v~Ekh62BXf$@!*~CUqSsp| z%Yw$dATdH^3vxow$5V(bH_ran5(fy&B2-A zW9;1>Cu?c0HAimpZp!3)XU@DoWtNyUB|hbLbl!{evdBNPZ^^jbxIkQB&BN$Hj>3jI zb6kD^hp`1B(53nwQ@llRBuUgy67RWx`gHC(XP(=_Bm*J6(f6DR_-$DUod`p zkr!F>=CSHWO)OwNfH&9U8eR!lt)|b%1jH06u0*3m+rvbI^i7$JLUH1LTn+jlmquE&z*{}fOv@@}F2jhPwM<;HYypNxHr>L&;Wa<& zHgCJDz41L`~gz{)QpJXDaVSogF#2Ue`ywGe`0bXjIeA)-n^AL zS!gR5&dJO5lbDzGtaDgu-a_;rnd7?N=4E7NWzSn=U24tA%0Sh78G2SG)TY?tW6M^E z=nupN%dEN7eY$6uV)_bbl)EA~+q!hY7}P6PF{5VRJ$V$3gk7^xv@%b&-7-U_J+y|6 zKgK>g)uRq|=UtC>ggXKMt{!!$L#oGYs$;KuyrM$g0esct5!5TIN7)FRw?!!aV(kBu zPTBC0|4p5WA#Vr3r%s@gPFa{}6Ti`|L+$@99bl95ZXK57XR(!99xvM=F|fNG{?j(r zU){__USx-rSxe_*z9rYq%K5Me_7lp0-b&VekSsPNn_H4O+Qj~!K5uDtA85q}t+_?Z z(dKuOvavuCzfy{Eu~Lj#s#6xkgqRIx@vBgs$hGGE1Kh9klkk5XZK-qI@no(}tNSEb z{3hAlmMj${rxYirIg+#Y{zP?-Be7QeMw?xyHMeP{_1ctYwP`!G*{{gzusr(vcY@`A zCRzMG*?cuwl9E$WlGD&_|IC$3);P}_M^*tp;OplPJcS~$9q3?$~3l#)?cK4c&9?A*y*t7=RO~V zs-yms^dE*!S^b}+|0s0IK8Iqjg~EV-vlTIsFc_j&g7)kzY3O2k8_f#n3G1}F0x(3 z-Pb2MsUC6YJ};%m-RBenGtPiTg=85G*a$Fljh0xz7(m<&xL1p~++hWG4EJ*JEsQP> z`Jb|JQW5eH^YK|t5|9e{2ZApbmI%t$)#z4V65MU`*`m^nqY>mG8><~Ll! zaNm45&f$lttLulUX&;~T#{5dM%vDR4JAf^KGQ8gn{2Rbn=yL>V3Q(?2%ojQ=gN9?D8(&p?L5(^^U1sB551wnhA7}m+_pC>1@cp%P>Kw#*yxPVT} zAm9f8&jNM;q-gx!LbPOY0PKJPIAkAzGMSODbgN`JF`C!PLpoZ6VFpD5r&hPlNYJ{c8ZR-{E_^Jy0bp#9<; z60VGm@)pasdTv+FtYkDKH3NHg=LZj8Fm1!iU=nzmn^j?->k>+ znY$dxvJG$uFen%40mr~w09*xmC-L5ie4atMn*T^XuB+Y!-V5M8o=0I$th!IKJcDq< zy$HA9Gf{{;V+B4^3mvZjs;O)ocWY-1yRY*^IOM_g3S0v^U6w|dYZk)b>(CuH;*?xH z7vVIbRa-chrP>8vBljI#vh|wF^@_`78l;m7((t*VP@S|LV^tEsz6!no`yW`1&&K>7 zpLdbq6H_tvK>r=_oaAynfjli-^Rt}vJ#>v(C0T|8s-f#%KsmqxH~<(^5GlM1?Sdu_ox=*^l7B4r?g13WsPL{ z1aKU10co27c_GLHAU2{;2VBX-XIl{e3IKhu#d-V3DMk)`y9FIH$16C*F#}A1SU@=6 zXc>s-?sbyo8sJGl2_O@29B>+O7N86f=o{bzG$>xv_;*@L7PMLN0ka9>b&`UUMCt`} z=&L)|;5>7!M#=`10}h~!bn-1M!#y5A0St=Tcw#^L_Suks>{5s2bAbPpTMie@P;iUe z1Gq;3Z@pByX@lKv-!dY3bCusC6YhQMs_O~ykqMuyKKfw%hdEsOrJEKFyZQZwj|y)7 z@I-BG?XV}mo3W=EHh?`)>m1#gqq}meL7NWHp`R1)!Ju7;J{~-}Ge>vl=pG$8iiGF> zj5bReH$a+;G7318Z-%TC=t$@DALIRNKq%gC1DwWt<3*RtfcIMgNu>j%B;XRnFUpiG zCiD@Rs3UU$`anmEWeIpI025I+t3h+U?Q&%kcDcSr_L}{8ziawk(~H(NTRv~KfaV9Q z{S&HbfT@dZb2TRO+)J4A?M2*cq;%S4%NEE~0l(4S;TobDqTw1{1L$0O&1XX<=TW>T z@SRr=qn}5gPz9WcxdfMnHt|=8L@#m-v^v0I&;`U#0_XsP0O)Fvnj5$Z|Na91oCDvx z_ji)zLBK)4d4K{mQ-Nd|yc+W$z%sy2z~_Jwk4u&x3o-5kKMwd3FzEM~9|4?zPry3| zeCHFG*WmqO;8MVDycd_Ep31P!b--`_3SW8w=lMHuCJr3CQzO+rhtF;OH_q&z2Y-)7 zLK}T)8gvN*oQI5iA)^yG0(dC&TMT+K@EG7c;6H=D1=xo0bHIlIPl0w4I1e}-a4X=N z4RZ{@dB6<7yMTj$zXINb?au%bk^dW=9hNWgzW+v@G~hDo1Y`9W%*nX3Xm7x=`0QLG zx>6DC@%_8!>$U|$*Flmc9OK>Fc>gD0DBi=+AJl;UE8v~L67WNS?SOv)wt#jSc#2N4 ztN~0$cnfe8@Lpj55XmwNuoEB+lq{=2zZxo8z5^s7oDMh)ID_y%fR6*40O^RgANYR2 zP5}KrSS|FP4mbr~^G%ZFQV3?rc<%swjc^0P`!xfkaFn$O@z6mOJOgY8WCO<7Fs=dT z0geH#0L}qU0vv$-fCYdQKqg@94U#2jFzgJR4|owU5%gogk0ZalKzz0j;r9?;f%i_} zhe5v;cAEuy8eldc7LW>hP8fK=kK$tFQ+OYS_j7oE7Vmy|Pr^mX=kb0qT(W$K_q}-k z2>5m2cfsEVSQEf;pCGJ&UZ)WbxDj(;;D6#W<{fw!!2d1qCy?>RK#p67_iEsm5e~tW z@XrX30e%zsFQ8q(Joy{EKY@1#E?RzocPDTp!fyj-0!y!Jq$R+wARqdzvLi9^76LP; z=BOv9wOL&tctMh7pa35SOvC%VzzY{50)F9C!BtP6i*OO(H9$CQq63UshVO@Z4 z2Xq1q2y@W+9B>KNTKKsX3x;?LzRJgRa-%(Um`A|B7tIE_As)_@Q|Geh*I=+>xXL_5Kh z!^NQ#c=;=!O&&Jh!0YG8Cjv!Oa>LNhMgSrK<^BVtg70w88Gd^T;bx548cc>YtsRyr zfc0pfe*%34-e0-WVcCN&^&8;3K{vE@STcY!5MBZNH1I^wCgD8`I12a-@X&Vbu>(dS z>;Mh}t_2?0(P7czJsJ24UgurE1<>5(Ck+B^Gv1>> zKLUIPaO(<;2LHtO2O!Ncz-p{(Da~#1ob61v6mL%Aszt0!H+|BvZTDt} zrtag8M>m9QJOtw5KGk3aD2 zM85R6c}PrYf&zq_4fuET2baJfg_}ZG=gS=pGlWtHXFJ$Yui%GKu!cI#}zn@SRUl%4h=>Dmpz7|E*J* z);lQLqyS;Ct(4xJ#T`Om%^x}(wbc#t{YKswiWni?&zP*XZKZhh@;JT3uj~*) zpK_AeL=tY>Ow?$F?(%SRR6&PHxo4(T3a$#!|Gwh{@BDp-?J45NDT}^r_%+DRRUq5e z5zU$qYVKH$-KD z2Mrq5DN}aq@Ulx8wNNINJ*o7>Ge1WaP`1_`r!<3`=~&==h^%8vqO2;^c%83ehEgfa z=Df5Roy-Xve$Dn^A%+)S z&WMhNrAqT|U9>~t7`}yk5%JIv%3e}gZ$z$K!yU>^gF2jp$Q*X9!!81E(Bj`zOhgek zY5-)a?30VakyD@{`pwRTd}Em-paH`p@oidC!Rgmw)6k2}pZ=FR6b*ya)ZSDSEJjoo zS4kl~M|lP8kw}FGu0T9jP~0$TEB~i(PT+?&Vn20tyVWv42s#l|74(#| z;a$Oc(%OcDaEKM2CzEHI?(dqEiT zl+$_xesfGI-K`RMA@m_$x^GQ|+GtW%sI4QF4ck>S-fu6h(b?6rZ`ct3;0&l!<`O_C z^9#a%V_49PTJXaeKmEbw#J>uDII_flfUm4z$)u-FhwMI(ZILzoiXz>P}3 zcIG>_|85sjo91G%QfKebji(B@98)|44;( z(;7NmLLg`xy=XRj`^Lge_V)D$N88rZ>w3Hn6T&yGYu~onxvu@$LqVIKY~NV(GD~u$A$$%R!kzRyrrn6wxmj zocvce@JB~;haI{qWrd>bN43-1UZt|Ox(+Ble3jGBYZqVDvHzms-K%jSVIwrohuUoq zkX|VHe8SE-?Y5bWptLep|39N(e2=F&G9J4qp&&Uo%!Mgw586O$peO;hYL(bRnYk2iNMRDwT6e zd&BqWT%4Bn6Ur*y#!+tTzI1UF3gZvZIBmDtVa|izGosp4Ue&Sx;>>ri=7)qWP&vo7 z+af7JN||Vjp%>?vcH0PgsqnXrV}#Mj&T!ae{o0jU*_wK(b+&%2GVJ~<$l2I#{K~cN zGZ*I++fVpcscm1hAtA<3M3{Wp?*kWX3L^@EI;Uv8sD7&Eu6-4Xh7&45s8YZY&%4G3 zpxwuFWg7#XH?*HLgjBxh5_cCEivm$0pdaQ3R9CC&UklkZpndPIg2K^U>TJb^`H=y; za%$I<^9Ke}1xB*o<2?WI!1C~&lQbV~8&zuJcD}AJ&)+bAS59O=>EZBQ`Kep39@-kG z9kt~>Bw5$!dOG0fC0BW7>Kxau`Gv>ABQ+axh|wbZTbV7;d9e-pjNV;V!gNXJY(KUc z9Ngg=p^7ipL`ETY!<~WlsD+Z%4nxD>_b(wL6OOu7m5g_ zURD~1a(CW|PJU-BpLUx#pC6F&YC&3>>QGFDU)hhYWS-j1gn{uNw#7wDF}mpa%7X@E zWBt)JDF87p!>aB!M)~mmQSZ3(N%e~x*pyPtI|cXz-;fz}(i zJ68#zJICXLCf)Kio_9?cNclz`Y%|}lvqwk$z0GXbrOXx|S~Y(^hwJ?pYL+v=YS*tIC$ByM}6$EWf zPTN+)alRzFW?NhJR$blFwyn{h418sC_-?yN<9xPl-?ME+2C885n>5y89K#gE`W0G6 zmkq3#M*N}8Fq{)d7ZeUxpm5PB+{RZA#mK&6v$qKw*o<+j#wtsNEmf1aEOUf7iAtpT!HHc-r-wX)ZCeajb^APx4V? zhf8;}!r_c}7{t9%@v=WZ6zE*q24}X;3p|+^Vq4rMB^5-fCJdS#Zi>s)B$-Cts(xUz zX4>D?X;Z}%qqtC|UdZ!)<7i@m4r%i{Dj|I51cjiH=BXPEA)-+{bf7UYq(VFD4)rUW z{iYp6uU^Hc&un`gdq zr}zuM%DB6H^n-V6gn{dCR|x!}mmd!}WI!)qyeH5Z)mCOjgMnS5ZXpAp+Kl%GUXBi~ z8`oCpsID2;<{a17@Yf*!`GIwa;Haq?-L|gXwMlFf_3>syh%>UyVPJErNX|K`4V`s? zL6oPThAb*r**tgOpx3uW)nAe8df%`%8r>Fgj?toL9wSxfnO_n?wk}n9Xy|iteUX+YX5vip7M*s9VXszIB5beB$y3_QGdEJItJ%BVxlP3hnjD7n3QcwlqhVbU&27n zUo~&WAFptU=7gEpsh*ND%aJfpF*grGMQg5OUV@MVJTQbNd4mn-bA>gZvL z_!U?DmGih5bM!m~q)L-v?%Ct!jOctRjyIpb6gSZP%_Swzhw{qdyefuQPv-sZuOzpBkNn%{(eJ%bb*@G+yt{s)N57eBMDM0wr855W);{zb-dn$bp89c9 zzk+U4mQx!!Ol_pn(?)dB-u8Tx^nHo+eV_F0Y<1i7{ZMtLM$z8d&7Q5TZhJ!C_q*%+ zk~03K|AD@rwf3;5Ti=DG@0+CWOI`ZLP(Nq;q}8seHBmoSl3%o;B<@**swC-I!}oUk zx;B)Q=FahTt)pT``)7m-N2aYY2qUBz@eaf4BdFbV9CzcFO@cc9?bdp0NWQ9e{gC?? z-r6VxOdZV~TUk3Xe?3Nkv}pxNs%grm?49D_bXtfTV^vrZAB!5l?KA%``Kfv?X}s-V z>&C=Q`K{9? zEE-g{l2$g`VB>@sE$aWP)nMQj@8!ytP$eV_Q7;5MR5Q z6t#ZNN>=+)1&^tbz)xihHGOjH_~?uJ2U|sBLArnIsN3|0w>oni3XG2!ct3G12}m8R zs+-kX>0D7WtJRs%S~fHYOA_P9F2}=S>LIS~Zm@n|b9d|1D=zCmm1Bo)=QEQ^NBU!a zSfw^L31qn|kMq)gnk*PUayjl4!vd?ED3;sjZ2rQE23^3y!G5&a0vbfa_GKNe*?jE>l@427$K{%i%g&C= z4eB7wL5#rxBL}}v*HOjT*ETOqRD)L5Aqau*)>Nn~SrfGeVd7IeI5p;4v|br@AKS88 z?g}&rg9N1*yTvS~9X;pbU>MZo!o+|EHO-x-(&mx%I}P&-lr!}#SNHklO6RRLpI>&K zzHGck4u$^2=!g98C^c}Y-*T0XLv`~*fBvTr*XuoaYWWTaK8rmy36L=!9><=_+N1oVWK+&;VAPk{I-%(Vu z`E~kTpf`Nup~t^;d36UiQc45j`0ekbsjksH6j1bO-OHCN9goz!eA)T8%VkXh=o;;V zdbx6T5FTamVLRiKotS>EfgoPcZz0Q%EfYlx4d|5lIRqPR&NypRx7X~Bf z7q(dy$A?W;nL+~%e>V&@g_bK94vcIl&>2MBb9@gJz z?hYvXn|9}*lh`R^Tc%}c8uVyf#C#f=nD3d*vr?m#`tZx-K^h*%I_^C~UO(uve466V z7rt1H;}o;|jKys=eGsK7Q(sSmE=8}s47T4z);~4euC+EOHmNU@B^_+*JDIa7DMl$f zeJa$_=5bh@tA2Bc?OF@O7269cO|*!!UdC>c!uq+^ZR^f~&c>FqYYK-rE55k}{TW_& z?q4{#IsV%g^kc@Wiq-kh5h-`6B586!>6~A+ly$Hq=aHnL-IL_=mZ=yeS^38*qRzIM z4F&i=>Lu(`+u<`cU$i*CXeoO|u$?F8k=Fw=K5G$F!a(zgEfAim9_bfU=_s!Iq^0J= z7VHz@gb&9Lw8$=dO}lV#NZ4fmO2@Xk_giZI(c*W3@**Wmv=L7&2|zEgRlDy*%L$xT z&3K~)X%g7MgY5+6R`lr?Z?_zFJXcrUvhQe1!(0BuS992O42yM@8MSGD7fT&n*?(!} zHP^Jzz`ClIO2>;e`&*9Ixvc+C#J|u||5C`_{RQajFSzO}Lk{gn=fXNL_>r6nJ4^-# zCk7D2_aF%EgRR|86($P!>6ta{(#c@smr=YJ92P` z95z(cXl>6>k*TVv4LVo0Kr3s#V*JAz=c6qRpX029EzH5Ibc`eqJ>}`sYR^V0r}L32 z{qg2VGW*cR>F-4<4UU&UDxfma91iKCo@g;Bxzr}VG%GjL$wjSdsZcijOOf-wh~C5B z_%Fq@!GlDCA!jsD$3O9nC1GF+@o{G%Kt1W0&)RmU=^oqqM+U zr@*L(eNyJ@>0E+?OUU4&9nu8-^!F!(L}NCmm=Gmd@txjw{rcx_z<$|`q?S3mk(cqG zik$;T4vFz=bH#<&qt*`>Ft40U`Qs#2&CU3A3nI_iT~@0YwT)O48EYUi8WJ;=+?M8w z-}7Ttf+|`4;EZIyX`wgW9lSiYCHwaUwwRWc2L>&VVedPeS02z{+Z%cM&E}Th(jsV| zR+iuRs6C|qv5-~2&-ee6|21x9$j(YT+Q(TV+Z2#7JoVJGFs~K~+ z##C7|0`B;wYqLJ!tezWxP*e7y%ivTTP2gs4nzMDtR`b^Ct?~8$6*6ad@ekGZ`1Qt+ z_5by-pqivYBuJb}AEP;G<^G}`builI-%@`tWaA54Lk>oLqL*`gU2&0bDG6zBYZ|~E zi27K6UJ}x5<2KH5U`|;Encu8)Ih2WBZm*_8)b_03J$CubTUHOi;A%UB_U@& z5i5dOQOf?NI1tqoh{8~TkEym>c5dfKMMPIReyBUzTywP9d9=CgVWIJlqGM0BIHp>B zdW+-gmd0{1>|1J>hqE}7YF9koB7^VRdRL+$kQ-ufpX9#^>d_jsrF2frZch0?l#Ylq zotQH#Z57S8C)mTM#f=ihmYGgP{NCm}W6WmqxP5!hhA$k> zjVgJxB(xOA?i-tH?9Fvso9&?oh99!8j}+}OMJCha$RyKbc8*xK&y{*fRkx|R(ka#a zp&7%lc(9-_kV`$!RXU^V?9DYBnw^EsWe*C4$6RR9h3w8k?UnisA@3ac)DEq8(MkR; z&5*4?>l$2fG1$9AzmbWFC+uK*w0UF9=$IL+np1GuJEj_k&*s>I@v%5cT&LyqKVFg? zEz_be3hH8Z^q8+I)g{yr6UZ zruyA4o~pa^z zmzu(6^0wKRFs}=XQy6a(9EZf3xaKpwGp^Y-{}LowCkTa3)%wk!pj5V7nn#&#vW;zC zUpl1FsaZepTieae>o>n{a4Of)8o(CWysiV-;PhKZofT{_iJx!q(~oSP@hddD3Z+S9 z8$qq`wX;Qc!uq&778wev+H-4TY`}%l_RuQ73Z>md#R!ND#oSqWS0bVeMAg*uI4KUB z(=oI$+NpL7U*ku|iSBu@Er=witD51Je7;}4f4x7~`E60zOoKwq5oa31Q&({L{LE3@ zOe0^VMC+|rbEj|LKIq+j75;dw-I%0|iyA+OcV5244Clv8$L8=hE;8y}r~dm(cHV9x zH8kAm0DDwPcu8_tvMOwgs$_Q2hOJ8Xh5}c@S7=JK^;$0V(6!2%i1H}=V0#kM)IMxq zUJ|w8!41l)025yx(e(WN&2BnV*?g#^AAi+U3(4n9JafC-BoHpUy_LS_Ni?4 zX$mzMhK3mwSTds;Zv0lOf9X6E))Q1PmUY>u0 zeNmWZX#UEuS0eD$GNXz zrcN1LGH3ij-Y<*0^S%dw7jcz(zr9<|rcStN?=xppKM4=Z(;7^{7iKO|J)|sOZWygF z7zU@F74*wd+vUqWr%YkHu~0sGH=J8z4l`VZemv7Wb(^|;d1Z~sG}yMUY17^&)7cG9 z?9zC2zc8o7`SHG!S>pDiRRNjHg<`tO=C7Yeb#QxMT;p)$rS4XY4V;mBsfwSi z#!xlWsDQm-_;)T-Pg0?+3OeDpVaC>wPolnwZ=+7yHiv4hinq_e01|aR{)*oJ;qa5s zhaC8L_Y21YN=GYp@7ej1df${w4@^{4@k16K#nG`4D7T%5n~r{iPaOLr ze@bvF#?rzP_AQS)ToZ9XkC;mW?8^(&`LKX%sP`!Jh?FI*S})8gkE&9p{e4T4URsfH z23~e7q|l^eygj2$JR0-B@+cH3F3L3AG&9W5UgcLFgRmd=jF+g^edofm5_>a;M=KCb zfW)Flakh?5&iJ*`9+G{3^(yn&plnYx2a}dlXFi~ z*_psG+TukSCr~@MlwtyGj% z!}V+CH#wI#*`B_LT%#Vn2+;}p8}T3WrSZ@3M5L+2l-bTK5CI!5(#c!UlcnVYO6{7?!Dy5DtWt)?l8<84oKHjxkP-lKFW4qGron^4A`FQ)seQ z23jv)wMTE>`Okul)$0!i?J^jg{zWQwb8%>+$sq2o)+?KKR-VV5L>{X;(#^orwlW%g zYAI*CO2RQADhhD#RbgJ(cCpZ;0NGs@TMNQeJ??M=?qJFLTJc;%rDox1zU(bc!}nK9 z=VL*FI;LopkF~>9b^%l@qp4{!+fjKwYrI~bvS0B+fbAwy!Z?C28_MG<=?yG29Ks-p zmA;CX5B8i#F4{hznC^0n3D~R< z7aanOaCx@fJ}ShHzww5Ny7-M#M*L zNre9Si{cMD#1FiL@HhEsGTp9sy6Hr<7v-97ujWD}F4l?ggz&HpIzg%5dhsl;5C(o* z=7`=>q16BBqItI>@P}{jPf5u`EPTOEjLs1w8eXJIm_QoF*+ELVk92#`E(8cSq|I5A zaz7~&m#?>9BxVdTC*;pK*|;-I75F@E>dKdO=59pRtAve-ng=r>kGXw%A{dS6#Mg7rD_P1qPj? zU5WBX~E;=WXtFYU$B?0wYbtkt{skV}( z4g^%csZSC4>cHfr?^R%l%UMWNu6vP%3=Z(B6 zjI$3H3`OUKgprW}n~z__My;SJHf^CTecP{C&q4PkhtQi_=V+vlHJf9j-@GUor4gdp zxWzV%_?XVW(>V>@VHzidJ+O_R`diL6m>4)R`t+tV0Ve{4-w20nVMIE^pWv(fgjKds z3c24yx;ScKqfKyg=sm#05$+Op+x%n>dslflRVrbd%|-1K(&+svz1LlAOp6JDX-q-3 z3yr9UFTQWAH;6UgQ-wzc5kmkKqQ=PrU03==`16W@l9Hpgx5%| z{uI&A^96s<#oyT|$yGqTnVpaSC&D^&oEZNk#hJh-siNu{u@dt$a1Yt6ciiXz> zq>o*22ber58GZ+vjq2QKgt><#I0{L*!DCQY|y=yx#@*Q=hMXeKZJb=KvP%t z_sh#dLLQp1xqxjF2n4qhuobKw10sSt9Y8^_c1BPT9i36fx{aMC!9qZs0xl%fPLn8Q zX&E7H6}3(iYN=Z6h;7{frD&90s@B@N=5CFN&Lf zJux{kGhw#B(=Y8KzjIkqS&n_ZVW=}lx_YHSVDzh2Zs7XPEssu6<}O?vm-~F8lHsrP zou3gI8#F5I1`nhCyqUHJ`7}9vZH}>FnT#TlETGDVg0R{6ZBC@{&Lu0ss`a%iR<8<( zgR^nHEuMtqFTUHAa6LAd7rT5^$_n~imsIw|-szHN9in1#hKBBG?__cf*ZmVjr7MQ2 zg(XWQ397~@O`H9c`g_lz4cI&|Kl%|mb$;%v;*fyRlli%0lRB2q%efMFE@@+;DrWJ} z`jnx$^&)q9ZNJzY19L(@Df*Ie$*|Mp2aC|58)H8=B2C;`Lx|w9oM3GUX z@);g2KJYYqP$?%LXB~RH^38F;mK$v&I77U$TYx?aiKF9dTsXU&ci0A&9YNJm50V?_ z(~Xe<^P9f({XXyZk9%iN=4m>X#~z|CFVxgZ&Yj!X*&?-l=@;WMbnc4m%<0bK(ql)F z?J^SQutZS8H)@>#sfMgT-JwX!!b|Z1hvu{IahSqIxhXdwy#C2+Y9hadSb-KS0Umf>U zX`@BAX4u`(=+LbmcDFWur^|J_ZSv8&oM9&?K(~T8ajNT4$1fxUOt**xFiUh2woQD4 z`UGTbhr`4qOz=73pW*;OJaWDXx9^DCq8OYk3KYwZ=1p;bh3efaGx-WV{bpze>#)&2 z%Pa8n2JQ;{{K4p8CY1UPmis{loSEYM7DvNa8W^wq5j_a`i??N28riTe1NJ;ow{>NM zDq5@1v$Vc^+U*5D8EARr?Se4Aym(Nia);}t-VPJ$p2KSx0Kl$%a zE)KH1Z9SwZyZsE_#YUty3iS%QKqN3kRL25ss+4B;IL5hJp-}C08*&od?481rK5of7 zJe~Mv`e}{&f?%uJ`^V@Bs%K$JOkUN${j+~ zzu(PU#~vw9D8D}l@Q}LlO<{c>xBdjL^oPODol>nytNVWNn4ww(P0E&Wkew+rQ*PB5 zQtmymQY%ER?DCPaTC&hDY^P=aL#Y04kcC()GXjXth+=8OyxELVHvuVoF*ISMj;k$mM4x&%(>cKJmiq^3(88(mN~i> zl7p24!~XM*r*o4~-9(xLY*%hU)&5ZeW;|QBY^3h`hKRW*O4cj8FEx&sEXP8q{p&>;6sZRIU?}Itqjs3loNh zp+uXo`VhEk*`a?QE764)A4B9p#wuD`Rg_uF4;|Z{y#{Rr?t!r8+hl%}WK+6-4H_UJ zUBXI@J{|ieHqd=WVj_jgj}0(hvf9}vye5y5mkmywXto|OKPOk04nC&Oa_hX5qc}0w z2XOHIvx^lIUn^B9%uVps@JrVYTKorN zx(85*TkrvnboCNLZARH?GAh}%L(d;3$w&-InF;=jD{aps|5#dncwl(^4!T(3hoP9- z0mY#o>v+%zlCc}L8s3=Y!ScG{bdVQxr&C+mbll3`k#uB^j^HzS!Ql3b@aOW^lJzlz__jCykqqDjk>#a9bDRXuL^9(O zMC*R-u|)kg((?Ro{KC?(!4h|RvdSa~OQ-(UziuNbc`J#GRZ;6h2T}TZ6@e@^m#y9- zZKG>_@L(PJ2rJuNG4Blk`K5A_qK1SSL@}QZ4DS;}njHk=2_kKrBrVRx=5bzf=|&A| z-Dvb!f0Xpo77rbFFik<}=t1pf3G+Rw^@m8h43FeT13C{< z*A_lf;&&@S;TD2ICKRq!{Ny_%C5zPUx7d(jFG;h zaOv(2BtUv-V;+u!(wrHcki*xb$ka&23LQgZq8Qs&34g06Sk!Uyn=jFrO*rS9@TkzQkv zmR=etF>3XvqAXtyl+W3bGUrS6VV~thV7FfLLO)R=4gNa2QCY<_0W_ z_^lRg@B0N*W$8bLn`&U2Dr;DWjDWSa_00jtHj**_fQu?skhZQLV7GrYK#{z3-Eh9Y z4%C_~%h5jo@q^a21CF&MQ~W-6r&#Xw|I#liy;*C?azj~E+ho?bIrPrWwC>MjL1`4~ zRu5pYg&tx0fdBc&mgE5^sk(m>cdXfy*&W(T$HNBs*L^Cw z_>MBw6O8MO0ZYb!?phy`Z2xhJ$AqDrCG&N8B**<3!8$E*gs!HW!zg((a`~4%u1qG` z<_}n29*75tk3*hM`xFYw-+r-`Iokqtzh|rOr3GhQ z>hWxnK=KL6Cpv`kw@BOW+;h$*ZSx%n|5|1lGoUk(HVD3gj2mtZ#xQR( zo0ua^t|^)QN;J2-7$uXKk<2tR-!eZk-6pasL-3iRbe;n^msR9aI^jU6p&!9kc6~I6 z8m{!$8zf+^5(WZZ%FkRO3ZhcxmJI|!TujiRQxK?iJ4sgXB*2c4F5BdwZ#sV;HOY{pp!5Fp7kb) zwot~1;VJa2V@}KA(EX8A&rCceKf&l;A#U*Xi?Gb}7Zp|ub@(~vsP8{AGu>uA)B0_{ z!yv=hy4*x7yi^2P$$q$aRY{3jX*c)Y0M<-dP0U{h&$LFQ=NR)iU$pa-&@!jLc0bTn9tEg6 zTW9wJ!gz*V5Sj1neVtG?eWP!f2h4QtmR1}$ife1Cq5#`>tf)SG0dJl2PiEGExwACyx4r!%XffqWNXCM6)ansHeB?6s1H z16cTFmogvlw*3LYHC}V0X+Z@6D`B)fPEyZ)o)bsI6ve!ROT2BrM|6Cd3ye3C!fSP| zJ54tc9ZU4u_A;XLi(H+l(k1C6!6iM{SEobhDltk!9T2r%?{i!yY4YEHg0b^Hs;~CMusqyX{vs_{ z;%a#17o5J*C#WSgZV{R{lcB>>gIh)qdx%)w7O!CsnTlIR4SPlrk6_qC5R}U|=a2$e zIyaqXt|XosUaa|tEXU+T0kb1EnS{jD^`-N2{G`br_oeegl%nKM`!WQ9{FG!F!?!xZ zmtM?pWVEIv2c~37Q<8&`Atlpi)%W<-ohO413<(@2IujxArTZ@laKDrP-KXbsHjka` z9;B>WCKvZ^{}CUAu75haL?=8i@f{_7mdZYzUoY|rUU)+39HfL-J^fd!8NUo(j^fW& zGoeUypOA3IvtdWy1`Ahrl=%FMiWsjtE;%dFjUfT*-F?Z$1dl1pspO@7>&3lrM<^-x ziBCi}<&KTw9}fC)``5`uee6!g8Hz0qsiTI^jE~|$PLI^LiWGF(hy@c!b-T+;Vj|6=$hnQ0_6@VA-Pr;yS!xG84xo9j_p*;|q$(H-p7x(5-VesVo5 zn|jSTlBD6F%yUxh}d- z+aj?K!y2Ng9J(L69-b2N?^813tbQUg;U_{sKg#f!exi#~e@%+~pM&cwGPn>vGT6Q({>=KG z-wv5CNNCm}`!5OMhwY@{zEdCT%?$4uE}dW|{|~P8^&v_+{umL4`~FFcv&W6QnqkIN z<_MS}nss}W2VKWI5n>Y%sqtPq~tLf~d5p{yfb)cbN$i(VH zXSo$4Uwv#|B7-Qfx_#nf50Vu8&_+XR94rv%KJV$wIWNwk1TeVTaBhc8neLacnLBkm*ksw|J6KCs@~Lty%*jP(75(Q@$Xp}kwA;e54kYPNM6W7odsG%deWIXLGV%Z>E{~b?a{@6P)4#nq^Nwb9)v$S{O4!9bHEngC? zGmO=VhH9ZRgbyiFS)8_0KpGhvOy&62#iaByUK8WUT3*eo4CZ5ZLB@ugDXCjP2Cq@* zztzQfmd+!dFi~kD`HdEpCXnA60z{3F8Q=c*hB<72xSee94I=+7S0 z?z!HE=X&9b79uK4j2GlbCk$be5!eH?J>^O36r>LD0CUFAbaOI0jZ)n+y-)-~xKSu3 zBG{-QOc%%|WcMZ0t$rFRrHr2~?7u`zwn9BepU= z=Kf+UW6mz#u$2sVWl8SJ+9}SRZWLbNE<7I)QJg^%lIIR-83MR5K0OpqLf!CT)-^Mf z{2x8)fAmCT5$Y*c5r#K2JA9ffB8zo$-4UF%$ls!2mO)XTVv>lFj&F%Mfb~5@|36cf zn)vX|fAWOG<~BYaQO!pp$@9n|v-`+;xr+18Dw;I1J3jIiJyaIC9DajFez+S>d1enq z%j6$+Bjlwa_kZ~-B+>RRik8UJdk`VBb4SWc>f@T7#~@rSw%;2A>!kI{z>r<@;N;!I!7UtM-3+j zlo1E(HN9L}oPGMV`^BHjXR;~id^wd5Hvfw~M6V{1mQVjpVa49fL#~^_{y0|+jKy-S zj_8>f8A!WH13Bdmn?k~@Y?xft!}!Xk{R$R&V2?WR|JMsRiN@UV?ooRWr;HKzBoKy9 zKwN}1T@%?9k>v{?S`d*%zEzA*L>4*mWrlkZeZ^44d*vQzZB7tPXHhNB%LP3mTF&c{ zDa|6c{5-1ZgKu^qhZ)Q2C8|IXRn zWd1{IS9imW?(CO%)v=6M_L7Ii72kc=y)>}3y&GQ%%zjel#cmX6LW=BV?E1yI(j6v= zmL-PEzU78dlat&=D%?v%ai01>H(|!*b11cyb#eaHEuuJYW1h$>x*;EnNpreeC5e(H zgmd;FYGl~4b)D>n>WfvysZ@KEpgYJq9_v;$KPABT=R|k*83Al}o>Ipg?G9n2MSD(g zt3^0PK|fSJ>!Fu(i*{5C_dU<;%#+SAbQ+|m#@kb+o%^L{O!X#dqi2Po2xs!$^&b=J z&!|d%zdI>IQtu|{R7BFaGet^@dd6c(Ts03;*W5}uUZ)(@JZCU*v{v(!lRR>~iGukL zF6E~Ji?N%bvo8>N4d7~Iy>y0nCOM!AzN}T0bU~ECO==cpjMyE5Mm;kQI`zcxs@pOU zyOb3#qk3SZhS$D~k-+i_b(jmoI@AR&v8{5|C3%6OXmf#>8kxXpG8ZCAFu8z5LiFaI zBJv_vG96yPW8APyR9s`AjouLdf%vIGMg*SSaU;JYa-9Z6+E`9C4bg6*s=#=<1)TB8 z^*21Jj=(ng@K5AnGcA-TlSAoWMj|q&YL}( z%)}4?0`IbcZW!F@ocI0@>6}BqO6Rt-r1Tm&!wWAvAFjW%uZg-KDFR@V&8I zv1x{3is+@0^I6NZZl(7lci&jl-PyjZJCpi77rFq&`h={sIC4(^J1S;U_uSq5C~rtt zQ88g~3!)V9q|BHQcsNlJCpg%+26qDuu|eZJZir)j1m!pQ)*yc+^C)(=AngW6f$%ON zLI?rr^{AS2f}X9;> zs^&-ds1zT8c1{q^xTa^*@xP;zkKM%1QyUkQ z?i0IEaMMqi5+s2I#CR!{`=P&p#|YEuLRy!5QyVAIXzmp+vIhAzW3_C|V_6# zAWKJo(3o}VIx}urVjdQumKFL_z1qaQ;uRT?32w$fRrm-N@UWc)=z>U~7AOo|F)G1F zQ)^H7x;QR22-^UA!iSA{<}jhdtT{MOv`SuvmAS{5f^ z1gm^wI4d zK*YxwHKR_Y=5DNxrMNjBr_;yEybY*a3 zY#}>5Z^1)HK>K31z<y#3!MMhvhc?_s)$Gt*s{jOrg$8U zJ(_Yfiz&7?EzQ0_>DJr|(-)TEr_ieV^Q}S~c{Po!8x5;&p?M3Qu6cR|3$iy0vckXe z%0stUI$00?N1U_mS8+|BxrP=6|mMofPvpK<5uO@)r9404R^Q70X4swm#1)lQ z>?4p;F<&?%vR+8ELVzzTB5Qg?7Wyok3+oYB7NFpwfaM$fL{xiY37QXcE!7HGuiUAG zBZ8T1Gke2!NMe@qXZBl}NwKR$0O__IGT=DM0-caMyEt)fh*Uv)cFY& zeLlJ?BFj4hQ-h%tO}S#jg{X%rUU5VMPtff;A%L+JNCMPAZ2<~}up2_kV?NXUa1$j) zyY0rn$cFPEzv1Vbm6M=?i^&#P1Y&fXhTue6y zQMM4?rL_85dyRCcPI{%*AT?j(*LDO{pIa}VQET4(x2iSa5bUCzHNuvO7!4$SlbmDC zq_ayXtG~eZ{u?;1wVAcx-CNTH-EskRN7jA8uH@Ci0nhe|#(0d7_k_Sr?4dU_Fxr_W zU_JwDe=!9cOq{M9-i9205yT7ZJ%X(v1KjYz<<)|-qfYR9}Znh zC%LnbdLXY3F{+2$&3}FL&nRP^$Hev;BArX`s_WyP<>1L=MVX> z0Ca`FU;#E7GDVtT``sz5SyVFjW6hW5#bW&xQR%_CzF2VeeuBd)Fa2JR5Fs|`om|SS zkfnT{E6%y_!jlAE+D3?n4(EHFCOPxbza(ojbY9(=C2VHT6NGv5>-++eKbXy*2^vDV z)s;NyCguzOtbmxla&CcEj=+zi0$NfS%0HFb@cCzHV_Y+@EZc5>$Fpj0Wc+;>(dsjC zoYOc)PQPb5cU!8JPnB1U$sBhRI@^K(r8dv5)yoTc%8f3vhR=_z=B8CV!W?J$foC~n zm#us-q9-9|Zo0qsEBS_>U-E>Z+{yDQw+d_Swj4X_*($hc5!{^lwlY`Pco!pDUZ>0u_^b& zXie5uxc9`ARM;&YPIIAETVsa-)}mTno6}q!qb;i8=Q+0_{IZe>Q#J;+$j}GFbyIp^Q6~Kb5>=XQtfx zl(T*9u?`$=ojKn5C0_R9(%R!p;NdA8hBhpG{2>#(Pjy&K}8yE7asm2r!$MF#*jO z!uZJ7+qBuAazObZubWv6lV8Jhd1brn1Q#}IXc%q#Jw3&@ z7CB4y+Di7yf!|Joz|Lst3{$5GmaNP72CMU&%((F9ZQ;}97^$66(xOOgyNV+1xV83q zJ0lD)%zH^VlqU_3((1N45vq;U!i;!!Hb=L~saH|{34DY9Vx!n7NZ=VZwe-Mj9kFsY zpM%d9QMxFAfIdy7-&}l9fUmP2Q?f3+w+wE^k;e%Vd!(L*vY5$+eYAJ4561%O(KJ;_Gypm4Sf&&)*c1V!L$b1UbkZwN@u*pQwTkb3hqIv(Fe ze(cHajI2aOdPG8OKx*d-X-33q+(fV#Jox9R@Kk$7M8LF=1H9{q@gI9cROT4YOFEZ4qiC+0m;JF}&$ zQ?aj3T4ZO?HAyS-UKPYz*31JW39&%kyhv!NMn^d3u-i2%AwPaU`Px7}-L~7~!h8X0tUTn`o%U^fr256KE^EglyC+2^qhzL_A!mN$v5nfvF4TF>!FEdCK zmpU1dtRhddYM-1+hWw8e5BRK1Q-MyiV*_tYTC#xueduOrifsy zQfx>&^#lr}MP&iiMS0K*&j(BX{( ztQS(J2ub;zGbTIq@QTX_+rZ^aLdj1MykQP!QYy>dq+XB{oA@4G&11SQYBt8m@;JC5 zW>dLeE_aL1Dt>OzR=+KgtCSI+Nwz+p^0{RC3NeY;8M%tT)o*8N=naoCH+(&A$R`Zt za_1v(e(IPTJdYdF2}4;v)TGF)RBBSH#|9V=NgHBMlyDt;Y?F)e#`bSdF1VKUB{ce20d1mFnlNt`}vU6ZVDKbGC?M+qz8Uqi@kvK9DhUTpb z4x2iJr0ylD6;D$u_N|~2z4Hm_?oRI=t*|QS2=kb#50QRssv;%WK_lHR?oC96+l zq9QuNs^CSK#Z?~$+00Dk4U#_PY5Ej)O={*)U&bLZRy1Qu;Hi>u-?YE+ECbg|{-8ET z+X63zKQGtJws%g$l0qb7BB9nY-4qg0wPuFNTw^Plr8ZZ|!)xuGGo-Z#@S0KUra~gj zfhBpnqatqljAA|wY-zo8J@9Ho)t?10RNQ`@85MY@;hXD$?RToy#=ii)C2?G0Yz{BU znDSGsI59CrWA@#}q4pSKA?|R%__2cE5Iws@kKNVLAr@0A`h3GtUmW znt8QIR5T30u2Ch$yAxvH5r+6etmHab6XzIt6~~W8G)BA^urvM{+~_C7cLs}jM09M< zfrSYvJ+T4PqEo_4e4%w8&8y#S3x7*ZzPyEY9GMlFAmD_TNxF+E->v+4M?F4Tv(zQ! z^6+RogqZMACSPW|NeaT|B}O}AcX*yGDmEb^#D|q}ddDc58}`q9ikhp+C!>?_Rh>&= zy$hsZyDQiGCE6Y$U8b-n1m$wUVPCDVuaIiu?c|+fx93O=vsY)y|9Ks&{}M$)q>2{D zO^i!l_?~KRlFT>Fnzb=p|H-=nM)tA|1 zw+X5{d+eS6lJ=T9d+>4>Yu_)ERgiLVq9HbrC9~nB>kHDYmbFcmznrgHqwVx9P#ViD zYt#P8lTZJ@BdN^tmx8?^`l>ZWZ^{79A%Z6}@MM)voQPK~!IMm7*IU-gjJq*z(G@D`*(lPNGd^_f&tCP(=ZHmcMq=~|(GuXpoZSMFvoq1d#v)l39qnQ^xixFMk1 zqoibG$!eMZRH|gPWZz_owmCc`kZ=#ry>0goq3YkUS92>A6}84lNS1I-oYPY0VV5D& z$T>v@rT)){y0bPq@lt*x|~E9Lda><~jcR@-^Z{jysAa%8W{R<-6BTPTth$m>yPqg1<7i+8<2 zJ5uc0r0U&OH9XVb@J%)s+6(`dn6)KNVNeXT9M_sWed7ze7_oOhSBP^xlFpvj~?qfO(=YQ{_gU^5m0Y+T7oY6GK57 z%b@<|NsPX0?rvU`H$FMo2oYYjg3u5#Z@`Up7&He?U3@^mXtnni=W+!&5D4x5-Q4a` z%-#%o0qrJ0@~2YMa|2EgaTMjpPtwkK#)4FNEh$a{QpdWSwk{VSfSKc_eVRoSxTyXC z^ivk9(&zyuE#XKf`V{zzRCmL;xCUc9MJ3E`SwuFY*{fXOEGNNyyVmyu6*CsZk8ZHD zA+M`is1&$T77CcXsEpaWcn^+Rki7iKso4p;FD)d@@TTPy*u6Edi*a+jKNzB+6}-uG zGX;xZfS#qJw%=j*w|XwMw5J?|({AvkIMk%wdM+;iz1sLP|L?~I zlP?BzPcmsgvETZD!vt*FDF`qWHCY!>P)a=^n&Q6S0yJL-UvrZ1f^_q{K>6)E1GcSS ztwF!sFCHrag+3|$+jr<(pkcUAN9a66737Xa3Ct9RAqC7N1&|CYzeA`y4-o?bRrvym z8qWXgI-euqWrRKMrOOzuhOJlkk4|vFap3byy~u}>lp`gvWA7cZ{dmU{%1Qw>Y3Z(_ z16EI}=qNA~V1Qj(3{Z?u&?ZO}pa7{_oIBn40|q`$GF0ppWrTJ?I4%KXW|SLK!fmoD z@xpLhc-2mIfWluT4v4IxXS)*wD9T}>{eg%Ch5r)tjQ=S}=8&x`=%!7E(Z9rCm*Wp1 z1(lu5lb8qJt$*;{CM^`$jNGAgX}NJ}l2Nw#^mv1}(TDXfVEuxfik*@1Kez~qJXaWz ze4;b+c;{ka*m}Bir7&o-P(~s)X~&Ub<^v-Cxk9hSLeG^#kIh2f{74u9a?{Kus_?mb zd2(InT;afCeQ;(i3c0aTAG%U_Ra^a9N#kqv6Ir;l=8Yxw8{Hml^~RFM5x4oZlKSC9 zgnqWlV$H5(Q)i~JbN#hz9@oWVu8)D>z30Sce+&{roa^UN`waoLW0v277oOrqjOj@T z=1i(hQxP5$!Hiel6mVVZxAp30^2}36kpPu{wz)4>MAJ zKrImJ<`2!`k@Fym+?7JIu>A~UHv%g53yg z+q=PQ1dM1~9qk|IvY~a)c4kiKT)zT6T|ef!gY@*G_)v-wdGxBr^hUfF=cCcfUmDtE z;F>(=*MdUQ#@UPa0HNYI>lqI8)kvqXUYG>Npjh4F_N;hPHjg8Y^S|+UQZ1y0+;e8! zdy8h1VR1z0yI443r7)z>6T8FtwVQ?i1QQ*2CB+8RHj`UhF*dO4m{gnJ4m(%F<0SUv z;}>h=FTpU|CahD#V-ZbT!HyTf`Qded96pBb7U1I(P(4KmtN8!H1zKt2UCm^FKw&s3 zPdq(dB;yH-LVpfzA2kYhmIqHj|w4`WxlpdAz7Ng(wi29Qe{F;AYXrq zZvD^Imhr9eok=H}lBRSWl{4ZnPT;#!#M|#PW13E%WB&EUe|(!xfBMBNe$(mG0-9<% z{q7gDo^Lu`v-pGO_D!OV^3doBGVrd&B}ej#U+LzY5PUIpluW5V;W2SCyJ%7;1ONxb zXP+xa>}8ZrR)mSBc(r^a0Mn~@yiB%i|HfjmI0}po*faj&K2w>0#g+Ejedf*lOT&HO zmI?*IPA(E4V+44v6q32D`9_9o5O5~-G+gQNOE}JghQl){M^)$RT&8HPN11>MC^&Nz zo$|WSd|Z-t{K@2to81@;n3T_A@W^I22J_e4U-{;E<*Ya$TG%HdpG;deqk8!?XPni?jTGR}RBW4vNOr z8!g{JQV92SL!Zk`aK?<_?DHR)%H|GN+MFMmH=Emrn@n&tf;V!v!wBA#KX9MotrGW| zjf7X;{Usb)w@>k=yhCh}ak2w`zAyu{Yaz}}ig6_FUNbOmWwEk|s)ovfCm`bH2GRM8 zD1G(I(C9q(rgVwnoB=Ovox-Qtml(n$7kUj%Akg=eyW|B$@1R&TwI`SFw9!!^il*KnfU?uhJz8^M>x( zGdNjiG(!t_Cyw9QyGp95_sx)}jG`9QCH(H<-f{DCTf{BTouym0&iwc+cdn9imOc|N zj%%JkEr?&?Bd9&%%s-A-caM9=G<%2q{a56SM5o0_6xq$-{ZDS6>mPb zn3yk|Hz%Jk6@FTI@TYkT99`Ga|6)I>Pk1?3I&Z;}xWqqpeHD1ipT^RYuq~>W@tz)x zgHtm1iCJsXpOy#LvhV&x)?B!MxK{bYu-kc&TghS9V$6%jF6RXGS< z_Zl=9acHvU$*Z0*eb?I0$ZuU!cVDZa<>$%k#Zr^DQrj)k? z;7jju1sp@xTY?&1%Q3RmbNn~o5b@)2Ixz@oW zMk92WuVL{br*IZ3H+k}<)u~y0s$icWAa(m4Oo_>jxJGRs4H;v-{$#;~P?IAg3_L>`%{PY7H`j zKC}Ls+;*)nFHE3H?%+*Kh3A9^4Ab*~8={1HMO$)YAVg9P* ziOLOt?c2rWU{G%V!40QhK{<_04&$*QS-K%vvLSgi+4-DfE(r~kp!+tLoTm}JH<`~Qs(ehS}>E+&!LX`)hJER83^0rMN*mS)u^m=&)ogZIkbWnq&%e`yZ~=2@|DZ zzrvtClhMt0x1~%fu>x&jgpW}Mv?<`w+AE~kA(J=GEC`E3tH|c5l2bkpoTv)H!fbO@ z`(z(>CFIGcUQ47^cNr*D*k#M})o!QEFbI7$eX5rz#PnyUCLF7)lG zIQ7nM6r|0gw%^H@unhI<3T}MO*NS;>D7bU1XI@v#<0!asih0l^Y4)HJt`9Aep5<2_ z#$Lz2_R+l6Cp@uz5jfv|vWgq_b;fl!ZH=`uhf51mARm*S)$%-YTdY9Jt`+6onF4?8d5L*NG|c(=WB++n{wOpS`LOc^&9g{Q6Kq`}fFB!Q z?3oq`%7VSZlQ!yu$u-vgg}k!hQRS+MEMYw0`oNPdSgZgDT?7Q>p`TcJjeimFw%^Ui z`l;fH6=A%TSW4v^pa}D2GrWxB130SVj67Iq**~NFm{eS2^Q5M~EebHy@Ctp&E=a#Z zk`foMKN**DGG4#JD`f@lqv0}srhA1qGJIRCPwRA6x$D%CI-i~kIt$3R3xq&r523}- z#}=yL_|+z}Qeu&1=RLu>q1B8V3CoX(aYFc#5JxM!#8X6?vaW!}ic(PZ#}0_klr5k% zWleK!H!xD?fo2u(DXJ)_Vz!N@tUSsVM_FTGw;s&zux_?uj2ZWtqVgy9YYD&Z-K!mZ zgHU6?r&mR>yaoa#zOaac^w=)${)Kw8Yi^@B!dw(hZ-0P2JU$1rVPx3-IVIyz`FE}v z7dUOB46D;rl=I|c-R!GGhRT<~ql;nP!2-||x>;~@qNZW%?B;z;@18xkTgVs-X@MN& zhUSJ5CdNbRDHFrFlWZq*@5tEo?a^IyI(?ldv!`C}B2eHONQl^Qz{ z|2LattRVoCur%Rnji_|)RT3=PypWu?n>k2)+Ai-4ylF5DnI>i88%#0np~ewOxi z*en!M7(|u}Z7}L5vvQz3j(|du_20M|i;Y4!<0*ST&#WRbVIr$#GJbX22}0ccEx+7> z5ZsM6)sLMw@^6XX0CR|b^6%P1#*UVEbQBx)r=Bn(d0U4zY~;@P(l0y06&#DO1G5?1s04oIv5sYNlFTjb%iNlX{J?{A z%jo=IYVr>JV)O)(-+Uzu;4*^W)6AR==1%6+f|r<2nE6(74r{(ZS@!6>c?Ywf4aqKj zWKfsHN$uZC&wV4)cF35?Y`F8SM;#u_OxCS@>yi1qbQ$87y8ZurBsX;|^^7%}0Thb` z*}>hqX#{S0Q3q?45qM&}R1I;1%rJV#d0i=q#-c!+9@9Ai&H$;Ng(ofrY-u&6G8(Nu%hHh*= z`Kww>U^8O`q`)tB@ra$KUtlU>8e#hc<6Hh$cYiXp)_hM}@yb)FriV&aSv zmPAS{DQ#ven}A@c@+X&$(^1NK8htZjv^=6s<~EG$#-sdbn*acz&Ppflwug9CB}|4@ z4e!CTZ@AQV+EEmmAO51B|8=Yt90=8B&w_3=on7gQfB-TA74+dryy^%Tiy|Ev;zNT= zUV89K;4X$?<|8{M=3O!|F=gP>uOgGr`Nv0RxszeXf`?G&tB;rkCz$i*3V4+8ctKR2 z%&_dG#NEQWD_?n3Ih|6rxf;D;?E}?WT=Caodk`DY?_5^1P^ow*|GZuOIRRLYYibP= zwZ1LfAXB%pAz76E15&Yyt7TVIkQkZD!6`U)_QBG6n3pgYP4>D!AE37c>vE`(5mvf5;<$&t1C4AeDdI zE;C5*o@kJN1gBN2|5kqU3Tu_ly_Oo=tp^Ewq^D)f5&{UjenpUh=w2sfTAr+o2lHiS z1E<6wla=x$Fl7eEZr^aXW3O+Li6aa3U8i8f4Kn%UHXJ9&O$3mqDs!a2WA?UB|Hy0d zE3JFl9edg>d)l*q;KyukXZ(>cotKo(gE^J>aBAgVx_K%K7B9;}EpsD?7p|Aa6t-s; zwl8B11RHso1?|gBBSu$=7LkFMxvtF}TR803lGN04Y=>AZ^I~2hK+spp#(z0sr z2{`4JBrPJzGlV(bqhncnQ7WOvJ>rD^pWT^j^p zM2?Cu#LT;5&cTv)Mfgd5_%~Ku_m2C~xsnqcyCuf_zD#VWttrZ50?w&p%4g8{g3mA% z9oIPtY{RstPSvhHd6`rtgy1NC!-VYG8AadvTWBCxCuhNU8mE4?T|U7L8PY}(T##?h zcwHGOQvgFnWIQwNrT!$5fgZ*Y&3a1jT=}h^*Zx~S=YRPBHH_0jSWHI=x~;=VHPAoi z_PfXWes9a8wzKqkGu800O?POdW$awpMXS2r^Y&6zTUDZ}vN2^UI;6Z zv)pNePDb_3vUF>Y3*xs+-k!SaOe!)DOKp2 zF<@vG`<72rnfM9a;4qoGk5;o-kX5kZ5;i=}p4eSO!s8#f68Y)f8ZdZbGrAK=Q2yOv zx+q-hyIPD{%9N@r3hWV#9%Y(%g4UFy-^z}iB=GR zAp%Yv;f}RO%A%h!*`jPP@P0p!W3qMINbI01YSfF7=OQILBWFYz=%T`C+w{iEHfw(C z!8XS`Z3GFYT$IsTKK6ZgpY5VszHa>&U14A1PVdHj&MT=@pB`x;D#r`IP(Gb-*-2~FYr~;X9QlTp*3|%&m zP9sN8WUhv5eE0KOBT@Dnj<`0<6fy?0%ZJCHMcX>H&GEZmjX{g9byAyS#_$+S;2ON= zHZnHLo2?;jj%g!faPk)zgO>bOMVsR}b__B*j|j!BQ&35PV-o3%?H_3jl6r%z(1)qp z{#8_0K;(n6=&q<|FLFH+%Jd3|i;-1z!q6AoarKe1h`(}7Wl?3rqps{h8R9^{8AiVd zQ5wlkLlj-KV@7mgS;2>wZI*XiecK$~q-&eM9B!jgUt;(5@5e=GlUhC69A0gfdsmc& z{0B`xM)M5k3owQ(lUuoM4v#i`mx0?zbEKr&w(~j@P-PMu6(wc7`oeIs4X--NK)8CB z_}0;(T@0=hExANIC$3*ooIA%Wehj9QiY796W%lVuzk;;pP=)>0(a(2z4c}cXdsCaT zf=O8+&N!H{Cu0Z0591YnlW`*>c=7H;nLOr2g}VDn^fdLYE75V}wTQe{Z2t_J!-}Q{ zVZQPkR~Rqr$(d(i79nh-!h8d5bb50p#(_Wx<6+zF%i=7@Cn!2t(KMv~h7@**6b9EK zyuy5=SIED;La;XgJDt(N>aR(X^CSsgi|}HTw2~z8xT6^3qgtP$U>48drJr96b9$EE z)}vP(UtX~sxZ>Q%H>cX?emn-M5nus0|MJ+-wIp(h0i5mhroyfaz2+ndwcu>*viuH=j?`Pv66hYKqn;=%Z}Y_XwFeQHmriE{a6bwd~$5OCemQ^-WQD`I8Im zK5jK#as2y=CI3q0TK<-%%`f;aJ}iX{$^AFSweG_O+(#T$S1?Oj%iuojICuq1Bu)!Y zayJ}$v~H_pXLFu=Bk_{|rHm-al1vmJiraSN3ui>;M9G(q?;M7c(crKfNhK z&#=};s`ZJU8RK`>)(|?Pav2&C5SgIgwvjYqtA5?z?Dq@)U+rL&IM{N}vtd@-r!E=g zrCG6pO9kdqniBfatWUqYK~1AIv(9rgv!d`j5f7!kH>;ZnOW>a>2(kqTd+=XM^TKv6 zB`h8^{~ zHwR_i$FmPledo`!&T2KY%y9pk`0<2)|8Q0pem_9kq4SzqGb3YW9gK*X^$q;f-lR00 z_yyZ^)}q%bO$2_^@oZFhX>K6A5pb_TJ>77x%*n~Qfw+D=n*9{@QIMCWAK~}lm+~~T zCg6DvZaIEu;8}-!>EIK-40jfuA*6M=Txb`ip{To`x?B-(KEN*rzm(neGT|)W`_%O; zet1I77UW-#e2t-A8dB~fmkViUQCkuACp?vW&8(gHJ&30s&wucA;c?=5jK>r4WAUsI zXl9Aw4}zPNvjo2f@kHWD!Sg3Pf5G!Q9#UViNHgms@-QkdO$UD0z`qRcM}YsCXRZ#R z4HO>N1NZ@{i-MBdDo@~VBJeRmsJTSKxsPV8nBk>aH}u1-Vmu{ycHlAK*@NdG9<;L) z#vUjNWTg=489e)+6>4bc1cY}#ob{quGix3mBhuzw4Vb+VzZ>EI7oJMg2V8u}BZq_( z87n-%M0S|O{lhG(^FI6Qa*f8uj9La|^kuL&vy5QS^#&S1xwvpW#PbE7Pw(*`&61(c zjoze98mcuwGfNw&nZ?25jb}ZcPMKzw3ctU@^D3TTq&H5`%-WCVB*xSVJPTqZntvj0 zJHpQXFUsBpE~;wnAK!Cf7={gU6A`dx1_lQNWd@XiG&YEcrln-0q#eKp(Jn{L^72y6 z3b)ans zdkC}vSQ*JAcY=0*<;Dzyij`=y5qNJXNS;JV@}Ve_Rz2KFT65r62(#a0Ci!`oPlNIi zZavJ4;l2md0aO73xE}_Mg1ZmQZh#k%8&u4D7tieh{sBDronI)S!hQ*1%&=btJ_p*K@nWvP?HWMo!1oS{>grP0Y1;ATRd zpxAq;2b5o0`$Uu6+n9I zLfENtin@vCz72EIG~_EN%hK?r88qt_QriSMSmlNsS`MteemD7PHQrx^vdLkR&%*5h zFpBr8ft+fDpMMZG#~`zutyWX91qNdc^VKuok?!$xRJcHM>(LMFf@eOCY7&iY?1~e5q@zaN>x^ zEUM*93@5Fw;5l!^qdb8107Nsj3X}#G0EDkdLV;<35l8`60DFMg&U?x6DPraY@OsD- zau|GWh54cv-X$9S2FyWlH-QF#ZuCsjmrXd{{^;9Xi))aE6ybd)Q`Chv{K5V$a0U1k=qBbFB1VTW`Op1K$pV(n}vBkui9_S$?2r1MR3aj3V;oO4k!ma zzYlwS59k441LCd+sNT2cEFzRTo9Y94+!I9wdIVvbfJ?wyylV^SMc`Lp6P`B;8kE2! z?|?Zz1@8=VG*C$Fps}xE$0q0!AO|P_)&iS=*qI0i`r%10$_QFPkc6Vfa|aNA1D;m{ zx6{BC;1b+=K}P_e*CE$w@T)=n1Ok8{Knf^hKEJ|k1fUX` zWFbHU0e}>+B{9h{pf3R%03A>c>;Wo)13(R6^haL~dK$O}+yp4Z6#`llCRW{2Bqdp`e)9DHNlDJs#C@YA5TLm23?lut6nyp??Et^41XS%6S9vo@XfLFHB~`BX4(I-2nS^hSKFT zreYq|+MrJM`XV^_oz_9+@^^CJm&h#4iv^^B4}f>1ic+Df;*VkS^JAGf?(bBx@G~!_ z26zEHXCR+m0(8JX(NC0vRsaWpLqHR77Pta*10#Si6?GKU2Q(B=0%^cfpa9qa=zu1m z4fqx41x5iHeNLe0$xdvzent&GIB5(=#6&MA? zX-FTC0vW&}KnZ97UO2w<1|@P4y&yw{ZMlKM!>m?;XX zUN9Lm`7q3;H+XyC-rK{nqd+voVk}@oINN-b2mHcAyqTZ{7`p*Po;Jbn5O5l}GRd1+ z0{1%LJ>UQk0GU{rGn8E3hjBqa#+wU~=fHMgE5-=~XW>pzxd-wLoXmQLN&ewUCi&mM zAV4Eu7o4S-7`UGW1|VC(Z_8*hk8*#CNj?lWC#VUu2eb@yC9uHv{VfCOgt9hRN9gh{>wu*0ZPYq9qh&)>6yN&X$_^#QY=Ws+Y8 zjz7mFzYF?4FlRZ&4xru0W$^3wJW&dk*Gu znA1T|g5CruNM(w0Z($#WLO@Z6g5Y-(e51+0iq2L{Nn_t3e(Gu7FH~`v7POC`5enTF@0hI-mxE0XLuA?X@>`&9gRTcGF#iSgDIgh$0YU&b;CICP7cemwV?EGh&^k~H z@CwYkfMdXq04b|&a3^K88I;PycnxX)4iv!7vhGwC=p5it_)Q003Cah(y%PBe^EuF~ zKpV`XKs3yYfkFV92V0)u%^ZVyE3tF(4l^ZWmSP;M`)LpSKSX4EtBFGs%igkP8TR7B~dh029yxGypMM@SeG123>0MOqj!fn{dC0XLN&h zuM{)ApfxC)-XhcoAl?__8u$;ueG|-7vJXW|aWLNtVn&KQ3I}v>-wM0}IbT5f0bm4i z+_$3L0qcOda5IAL1wI8%14F>fB5!8m+u#G(473z`Gk*vD9`M88T~I+W;|_Q>I^7b_41WtH+KH^tVankQ)e>3_4{yWC^Ge?uDRRfdYhW0EIe26~b%+ zk8BD>k$I0_;C>efMmz~{6XuK-Z-#31W|aT%W{l0=&|kfo(ME43{v3;p1pb@g zm(~Qp{}QMeetS-NGglBV9&`k>0`6M@3U~qb?h|ltfE_Y66VD=YnaJpwFk>bbx!4OC ziQ4)oWC>T5iRCTSXk;TXl1}iUFvX`!%sZkEQ;UX@Um|Hq-O!)7QIK

RjKvb>bdhjqt6flG1&{n{AROAK!84<9~co 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 >5E;h@ayRM6{KF>L1Ej!vx;O7Xs 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 +* Distributed under BSD license +* Visit https://freematics.com/products for hardware information +* Visit https://hub.freematics.com to view live and history telemetry data +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +******************************************************************************/ + +#include +#include +#include "config.h" +#include "telestore.h" +#include "teleclient.h" +#include "telemesh.h" +#if BOARD_HAS_PSRAM +#include "esp32/himem.h" +#endif +#include "driver/adc.h" +#include "nvs_flash.h" +#include "nvs.h" +#if ENABLE_OLED +#include "FreematicsOLED.h" +#endif + +#if SERVER_ENCRYPTION_ENABLE == 1 +#include "telecrypt.h" +#endif + +// states +#define STATE_STORAGE_READY 0x1 +#define STATE_OBD_READY 0x2 +#define STATE_GPS_READY 0x4 +#define STATE_MEMS_READY 0x8 +#define STATE_NET_READY 0x10 +#define STATE_CELL_CONNECTED 0x20 +#define STATE_WIFI_CONNECTED 0x40 +#define STATE_WORKING 0x80 +#define STATE_STANDBY 0x100 + +typedef struct { + byte pid; + byte tier; + int value; + uint32_t ts; +} PID_POLLING_INFO; + +PID_POLLING_INFO obdData[]= { + {PID_SPEED, 1}, + {PID_RPM, 1}, + {PID_THROTTLE, 1}, + {PID_ENGINE_LOAD, 1}, + {PID_FUEL_PRESSURE, 2}, + {PID_TIMING_ADVANCE, 2}, + {PID_COOLANT_TEMP, 3}, + {PID_INTAKE_TEMP, 3}, +}; + +CBufferManager bufman; +Task subtask; + +#if ENABLE_MEMS +float accBias[3] = {0}; // calibrated reference accelerometer data +float accSum[3] = {0}; +float acc[3] = {0}; +float gyr[3] = {0}; +float mag[3] = {0}; +uint8_t accCount = 0; +#endif +int deviceTemp = 0; + +// config data +char apn[32]; +#if ENABLE_WIFI +char wifiSSID[32] = WIFI_SSID; +char wifiPassword[32] = WIFI_PASSWORD; +#endif +nvs_handle_t nvs; + +// live data +String netop; +String ip; +int16_t rssi = 0; +int16_t rssiLast = 0; +char vin[18] = {0}; +uint16_t dtc[6] = {0}; +float batteryVoltage = 0; +GPS_DATA* gd = 0; + +char devid[12] = {0}; +char isoTime[32] = {0}; + +// stats data +uint32_t lastMotionTime = 0; +uint32_t timeoutsOBD = 0; +uint32_t timeoutsNet = 0; +uint32_t lastStatsTime = 0; + +int32_t syncInterval = SERVER_SYNC_INTERVAL * 1000; +int32_t dataInterval = 1000; + +#if STORAGE != STORAGE_NONE +int fileid = 0; +uint16_t lastSizeKB = 0; +#endif + +byte ledMode = 0; + +bool serverSetup(IPAddress& ip); +void serverProcess(int timeout); +void processMEMS(CBuffer* buffer); +bool processGPS(CBuffer* buffer); +void processBLE(int timeout); + +class State { +public: + bool check(uint16_t flags) { return (m_state & flags) == flags; } + void set(uint16_t flags) { m_state |= flags; } + void clear(uint16_t flags) { m_state &= ~flags; } + uint16_t m_state = 0; +}; + +FreematicsESP32 sys; + +class OBD : public COBD +{ +protected: + void idleTasks() + { + // do some quick tasks while waiting for OBD response +#if ENABLE_MEMS + processMEMS(0); +#endif + processBLE(0); + } +}; + +OBD obd; + +MEMS_I2C* mems = 0; + +#if STORAGE == STORAGE_SPIFFS +SPIFFSLogger logger; +#elif STORAGE == STORAGE_SD +SDLogger logger; +#endif + +#if SERVER_PROTOCOL == PROTOCOL_UDP +TeleClientUDP teleClient; +#else +TeleClientHTTP teleClient; +#endif + +#if ENABLE_OLED +OLED_SH1106 oled; +#endif + +State state; + +void printTimeoutStats() +{ + Serial.print("Timeouts: OBD:"); + Serial.print(timeoutsOBD); + Serial.print(" Network:"); + Serial.println(timeoutsNet); +} + +void beep(int duration) +{ + // turn on buzzer at 2000Hz frequency + sys.buzzer(2000); + delay(duration); + // turn off buzzer + sys.buzzer(0); +} + +#if LOG_EXT_SENSORS +void processExtInputs(CBuffer* buffer) +{ +#if LOG_EXT_SENSORS == 1 + uint8_t levels[2] = {(uint8_t)digitalRead(PIN_SENSOR1), (uint8_t)digitalRead(PIN_SENSOR2)}; + buffer->add(PID_EXT_SENSORS, ELEMENT_UINT8, levels, sizeof(levels), 2); +#elif LOG_EXT_SENSORS == 2 + uint16_t reading[] = {adc1_get_raw(ADC1_CHANNEL_0), adc1_get_raw(ADC1_CHANNEL_1)}; + Serial.print("GPIO0:"); + Serial.print((float)reading[0] * 3.15 / 4095 - 0.01); + Serial.print(" GPIO1:"); + Serial.println((float)reading[1] * 3.15 / 4095 - 0.01); + buffer->add(PID_EXT_SENSORS, ELEMENT_UINT16, reading, sizeof(reading), 2); +#endif +} +#endif + +/******************************************************************************* + HTTP API +*******************************************************************************/ +#if ENABLE_HTTPD +int handlerLiveData(UrlHandlerParam* param) +{ + char *buf = param->pucBuffer; + int bufsize = param->bufSize; + int n = snprintf(buf, bufsize, "{\"obd\":{\"vin\":\"%s\",\"battery\":%.1f,\"pid\":[", vin, batteryVoltage); + uint32_t t = millis(); + for (int i = 0; i < sizeof(obdData) / sizeof(obdData[0]); i++) { + n += snprintf(buf + n, bufsize - n, "{\"pid\":%u,\"value\":%d,\"age\":%u},", + 0x100 | obdData[i].pid, obdData[i].value, (unsigned int)(t - obdData[i].ts)); + } + n--; + n += snprintf(buf + n, bufsize - n, "]}"); +#if ENABLE_MEMS + if (accCount) { + n += snprintf(buf + n, bufsize - n, ",\"mems\":{\"acc\":[%d,%d,%d],\"stationary\":%u}", + (int)((accSum[0] / accCount - accBias[0]) * 100), (int)((accSum[1] / accCount - accBias[1]) * 100), (int)((accSum[2] / accCount - accBias[2]) * 100), + (unsigned int)(millis() - lastMotionTime)); + } +#endif + if (gd && gd->ts) { + n += snprintf(buf + n, bufsize - n, ",\"gps\":{\"utc\":\"%s\",\"lat\":%f,\"lng\":%f,\"alt\":%f,\"speed\":%f,\"sat\":%d,\"age\":%u}", + isoTime, gd->lat, gd->lng, gd->alt, gd->speed, (int)gd->sat, (unsigned int)(millis() - gd->ts)); + } + buf[n++] = '}'; + param->contentLength = n; + param->contentType=HTTPFILETYPE_JSON; + return FLAG_DATA_RAW; +} +#endif + +/******************************************************************************* + Reading and processing OBD data +*******************************************************************************/ +#if ENABLE_OBD +void processOBD(CBuffer* buffer) +{ + static int idx[2] = {0, 0}; + int tier = 1; + for (byte i = 0; i < sizeof(obdData) / sizeof(obdData[0]); i++) { + if (obdData[i].tier > tier) { + // reset previous tier index + idx[tier - 2] = 0; + // keep new tier number + tier = obdData[i].tier; + // move up current tier index + i += idx[tier - 2]++; + // check if into next tier + if (obdData[i].tier != tier) { + idx[tier - 2]= 0; + i--; + continue; + } + } + byte pid = obdData[i].pid; + if (!obd.isValidPID(pid)) continue; + int value; + if (obd.readPID(pid, value)) { + obdData[i].ts = millis(); + obdData[i].value = value; + buffer->add((uint16_t)pid | 0x100, ELEMENT_INT32, &value, sizeof(value)); + } else { + timeoutsOBD++; + printTimeoutStats(); + break; + } + if (tier > 1) break; + } + int kph = obdData[0].value; + if (kph >= 2) lastMotionTime = millis(); +} +#endif + +bool initGPS() +{ + // start GNSS receiver + if (sys.gpsBeginExt()) { + Serial.println("GNSS:OK(E)"); + } else if (sys.gpsBegin()) { + Serial.println("GNSS:OK(I)"); + } else { + Serial.println("GNSS:NO"); + return false; + } + return true; +} + +bool processGPS(CBuffer* buffer) +{ + static uint32_t lastGPStime = 0; + static uint32_t lastGPStick = 0; + static float lastGPSLat = 0; + static float lastGPSLng = 0; + + if (!gd) { + lastGPStime = 0; + lastGPSLat = 0; + lastGPSLng = 0; + } +#if GNSS == GNSS_STANDALONE + if (state.check(STATE_GPS_READY)) { + // read parsed GPS data + if (!sys.gpsGetData(&gd)) { + return false; + } + } +#else + if (!teleClient.cell.getLocation(&gd)) { + return false; + } +#endif + + + if (!gd || lastGPStime == gd->time || (gd->lng == 0 && gd->lat == 0)) { +#if GNSS_RESET_TIMEOUT + if (millis() - lastGPStick > GNSS_RESET_TIMEOUT * 1000) { + sys.gpsEnd(); + delay(50); + initGPS(); + lastGPStick = millis(); + } +#endif + return false; + } + lastGPStick = millis(); + + if ((lastGPSLat || lastGPSLng) && (abs(gd->lat - lastGPSLat) > 0.001 || abs(gd->lng - lastGPSLng) > 0.001)) { + // invalid coordinates data + lastGPSLat = 0; + lastGPSLng = 0; + return false; + } + lastGPSLat = gd->lat; + lastGPSLng = gd->lng; + + float kph = gd->speed * 1.852f; + if (kph >= 2) lastMotionTime = millis(); + + if (buffer) { + buffer->add(PID_GPS_TIME, ELEMENT_UINT32, &gd->time, sizeof(uint32_t)); + buffer->add(PID_GPS_LATITUDE, ELEMENT_FLOAT, &gd->lat, sizeof(float)); + buffer->add(PID_GPS_LONGITUDE, ELEMENT_FLOAT, &gd->lng, sizeof(float)); + buffer->add(PID_GPS_ALTITUDE, ELEMENT_FLOAT_D1, &gd->alt, sizeof(float)); /* m */ + buffer->add(PID_GPS_SPEED, ELEMENT_FLOAT_D1, &kph, sizeof(kph)); + buffer->add(PID_GPS_HEADING, ELEMENT_UINT16, &gd->heading, sizeof(uint16_t)); + if (gd->sat) buffer->add(PID_GPS_SAT_COUNT, ELEMENT_UINT8, &gd->sat, sizeof(uint8_t)); + if (gd->hdop) buffer->add(PID_GPS_HDOP, ELEMENT_UINT8, &gd->hdop, sizeof(uint8_t)); + } + + // generate ISO time string + char *p = isoTime + sprintf(isoTime, "%04u-%02u-%02uT%02u:%02u:%02u", + (unsigned int)(gd->date % 100) + 2000, (unsigned int)(gd->date / 100) % 100, (unsigned int)(gd->date / 10000), + (unsigned int)(gd->time / 1000000), (unsigned int)(gd->time % 1000000) / 10000, (unsigned int)(gd->time % 10000) / 100); + unsigned char tenth = (gd->time % 100) / 10; + if (tenth) p += sprintf(p, ".%c00", '0' + tenth); + *p = 'Z'; + *(p + 1) = 0; + + Serial.print("[GPS] "); + Serial.print(gd->lat, 6); + Serial.print(' '); + Serial.print(gd->lng, 6); + Serial.print(' '); + Serial.print((int)kph); + Serial.print("km/h"); + Serial.print(" SATS:"); + Serial.print(gd->sat); + Serial.print(" HDOP:"); + Serial.print(gd->hdop); + Serial.print(" Course:"); + Serial.print(gd->heading); + + Serial.print(' '); + Serial.println(isoTime); + //Serial.println(gd->errors); + lastGPStime = gd->time; + return true; +} + +bool waitMotionGPS(int timeout) +{ + unsigned long t = millis(); + lastMotionTime = 0; + do { + serverProcess(100); + if (!processGPS(0)) continue; + if (lastMotionTime) return true; + } while (millis() - t < timeout); + return false; +} + +#if ENABLE_MEMS +void processMEMS(CBuffer* buffer) +{ + if (!state.check(STATE_MEMS_READY)) return; + + // load and store accelerometer data + float temp; +#if ENABLE_ORIENTATION + ORIENTATION ori; + if (!mems->read(acc, gyr, mag, &temp, &ori)) return; +#else + if (!mems->read(acc, gyr, mag, &temp)) return; +#endif + deviceTemp = (int)temp; + + accSum[0] += acc[0]; + accSum[1] += acc[1]; + accSum[2] += acc[2]; + accCount++; + + if (buffer) { + if (accCount) { + float value[3]; + value[0] = accSum[0] / accCount - accBias[0]; + value[1] = accSum[1] / accCount - accBias[1]; + value[2] = accSum[2] / accCount - accBias[2]; + buffer->add(PID_ACC, ELEMENT_FLOAT_D2, value, sizeof(value), 3); +/* + Serial.print("[ACC] "); + Serial.print(value[0]); + Serial.print('/'); + Serial.print(value[1]); + Serial.print('/'); + Serial.println(value[2]); +*/ +#if ENABLE_ORIENTATION + value[0] = ori.yaw; + value[1] = ori.pitch; + value[2] = ori.roll; + buffer->add(PID_ORIENTATION, ELEMENT_FLOAT_D2, value, sizeof(value), 3); +#endif +#if 0 + // calculate motion + float motion = 0; + for (byte i = 0; i < 3; i++) { + motion += value[i] * value[i]; + } + if (motion >= MOTION_THRESHOLD * MOTION_THRESHOLD) { + lastMotionTime = millis(); + Serial.print("Motion:"); + Serial.println(motion); + } +#endif + } + accSum[0] = 0; + accSum[1] = 0; + accSum[2] = 0; + accCount = 0; + } +} + +void calibrateMEMS() +{ + if (state.check(STATE_MEMS_READY)) { + accBias[0] = 0; + accBias[1] = 0; + accBias[2] = 0; + int n; + unsigned long t = millis(); + for (n = 0; millis() - t < 1000; n++) { + float acc[3]; + if (!mems->read(acc)) continue; + accBias[0] += acc[0]; + accBias[1] += acc[1]; + accBias[2] += acc[2]; + delay(10); + } + accBias[0] /= n; + accBias[1] /= n; + accBias[2] /= n; + Serial.print("ACC BIAS:"); + Serial.print(accBias[0]); + Serial.print('/'); + Serial.print(accBias[1]); + Serial.print('/'); + Serial.println(accBias[2]); + } +} +#endif + +void printTime() +{ + time_t utc; + time(&utc); + struct tm *btm = gmtime(&utc); + if (btm->tm_year > 100) { + // valid system time available + char buf[64]; + sprintf(buf, "%04u-%02u-%02u %02u:%02u:%02u", + 1900 + btm->tm_year, btm->tm_mon + 1, btm->tm_mday, btm->tm_hour, btm->tm_min, btm->tm_sec); + Serial.print("UTC:"); + Serial.println(buf); + } +} + +/******************************************************************************* + Initializing all data logging components +*******************************************************************************/ +void initialize() +{ + // dump buffer data + bufman.purge(); + +#if ENABLE_MEMS + if (state.check(STATE_MEMS_READY)) { + calibrateMEMS(); + } +#endif + +#if GNSS == GNSS_STANDALONE + if (!state.check(STATE_GPS_READY)) { + if (initGPS()) { + state.set(STATE_GPS_READY); + } + } +#endif + +#if ENABLE_OBD + // initialize OBD communication + if (!state.check(STATE_OBD_READY)) { + timeoutsOBD = 0; + if (obd.init()) { + Serial.println("OBD:OK"); + state.set(STATE_OBD_READY); +#if ENABLE_OLED + oled.println("OBD OK"); +#endif + } else { + Serial.println("OBD:NO"); + //state.clear(STATE_WORKING); + //return; + } + } +#endif + +#if SERVER_ENCRYPTION_ENABLE == 1 + Serial.println("CHACHA:YES"); +#endif + +#if STORAGE != STORAGE_NONE + if (!state.check(STATE_STORAGE_READY)) { + // init storage + if (logger.init()) { + state.set(STATE_STORAGE_READY); + } + } + if (state.check(STATE_STORAGE_READY)) { + fileid = logger.begin(); + } +#endif + + // re-try OBD if connection not established +#if ENABLE_OBD + if (state.check(STATE_OBD_READY)) { + char buf[128]; + if (obd.getVIN(buf, sizeof(buf))) { + memcpy(vin, buf, sizeof(vin) - 1); + Serial.print("VIN:"); + Serial.println(vin); + } + int dtcCount = obd.readDTC(dtc, sizeof(dtc) / sizeof(dtc[0])); + if (dtcCount > 0) { + Serial.print("DTC:"); + Serial.println(dtcCount); + } +#if ENABLE_OLED + oled.print("VIN:"); + oled.println(vin); +#endif + } +#endif + + // check system time + printTime(); + + lastMotionTime = millis(); + state.set(STATE_WORKING); + +#if ENABLE_OLED + delay(1000); + oled.clear(); + oled.print("DEVICE ID: "); + oled.println(devid); + oled.setCursor(0, 7); + oled.print("Packets"); + oled.setCursor(80, 7); + oled.print("KB Sent"); + oled.setFontSize(FONT_SIZE_MEDIUM); +#endif +} + +void showStats() +{ + uint32_t t = millis() - teleClient.startTime; + char buf[32]; + sprintf(buf, "%02u:%02u.%c ", t / 60000, (t % 60000) / 1000, (t % 1000) / 100 + '0'); + Serial.print("[NET] "); + Serial.print(buf); + Serial.print("| Packet #"); + Serial.print(teleClient.txCount); + Serial.print(" | Out: "); + Serial.print(teleClient.txBytes >> 10); + Serial.print(" KB | In: "); + Serial.print(teleClient.rxBytes); + Serial.print(" bytes | "); + Serial.print((unsigned int)((uint64_t)(teleClient.txBytes + teleClient.rxBytes) * 3600 / (millis() - teleClient.startTime))); + Serial.print(" KB/h"); + + Serial.println(); +#if ENABLE_OLED + oled.setCursor(0, 2); + oled.println(timestr); + oled.setCursor(0, 5); + oled.printInt(teleClient.txCount, 2); + oled.setCursor(80, 5); + oled.printInt(teleClient.txBytes >> 10, 3); +#endif +} + +bool waitMotion(long timeout) +{ +#if ENABLE_MEMS + unsigned long t = millis(); + if (state.check(STATE_MEMS_READY)) { + do { + // calculate relative movement + float motion = 0; + float acc[3]; + if (!mems->read(acc)) continue; + if (accCount == 10) { + accCount = 0; + accSum[0] = 0; + accSum[1] = 0; + accSum[2] = 0; + } + accSum[0] += acc[0]; + accSum[1] += acc[1]; + accSum[2] += acc[2]; + accCount++; + for (byte i = 0; i < 3; i++) { + float m = (acc[i] - accBias[i]); + motion += m * m; + } +#if ENABLE_HTTTPD + serverProcess(100); +#endif + processBLE(100); + // check movement + if (motion >= MOTION_THRESHOLD * MOTION_THRESHOLD) { + //lastMotionTime = millis(); + Serial.println(motion); + return true; + } + } while (state.check(STATE_STANDBY) && ((long)(millis() - t) < timeout || timeout == -1)); + return false; + } +#endif + serverProcess(timeout); + return false; +} + +/******************************************************************************* + Collecting and processing data +*******************************************************************************/ +void process() +{ + uint32_t startTime = millis(); + + CBuffer* buffer = bufman.getFree(); + buffer->state = BUFFER_STATE_FILLING; + +#if ENABLE_OBD + // process OBD data if connected + if (state.check(STATE_OBD_READY)) { + processOBD(buffer); + if (obd.errors >= MAX_OBD_ERRORS) { + if (!obd.init()) { + Serial.println("[OBD] ECU OFF"); + state.clear(STATE_OBD_READY | STATE_WORKING); + return; + } + } + } else if (obd.init(PROTO_AUTO, true)) { + state.set(STATE_OBD_READY); + Serial.println("[OBD] ECU ON"); + } +#endif + + if (rssi != rssiLast) { + int val = (rssiLast = rssi); + buffer->add(PID_CSQ, ELEMENT_INT32, &val, sizeof(val)); + } +#if ENABLE_OBD + if (sys.devType > 12) { + batteryVoltage = (float)(analogRead(A0) * 45) / 4095; + } else { + batteryVoltage = obd.getVoltage(); + } + if (batteryVoltage) { + uint16_t v = batteryVoltage * 100; + buffer->add(PID_BATTERY_VOLTAGE, ELEMENT_UINT16, &v, sizeof(v)); + } +#endif + +#if LOG_EXT_SENSORS + processExtInputs(buffer); +#endif + +#if ENABLE_MEMS + processMEMS(buffer); +#endif + + processGPS(buffer); + + if (!state.check(STATE_MEMS_READY)) { + deviceTemp = readChipTemperature(); + } + buffer->add(PID_DEVICE_TEMP, ELEMENT_INT32, &deviceTemp, sizeof(deviceTemp)); + + buffer->timestamp = millis(); + buffer->state = BUFFER_STATE_FILLED; + + // display file buffer stats + if (startTime - lastStatsTime >= 3000) { + bufman.printStats(); + lastStatsTime = startTime; + } + +#if STORAGE != STORAGE_NONE + if (state.check(STATE_STORAGE_READY)) { + buffer->serialize(logger); + uint16_t sizeKB = (uint16_t)(logger.size() >> 10); + if (sizeKB != lastSizeKB) { + logger.flush(); + lastSizeKB = sizeKB; + Serial.print("[FILE] "); + Serial.print(sizeKB); + Serial.println("KB"); + } + } +#endif + + const int dataIntervals[] = DATA_INTERVAL_TABLE; +#if ENABLE_OBD || ENABLE_MEMS + // motion adaptive data interval control + const uint16_t stationaryTime[] = STATIONARY_TIME_TABLE; + unsigned int motionless = (millis() - lastMotionTime) / 1000; + bool stationary = true; + for (byte i = 0; i < sizeof(stationaryTime) / sizeof(stationaryTime[0]); i++) { + dataInterval = dataIntervals[i]; + if (motionless < stationaryTime[i] || stationaryTime[i] == 0) { + stationary = false; + break; + } + } + if (stationary) { + // stationery timeout + Serial.print("Stationary for "); + Serial.print(motionless); + Serial.println(" secs"); + // trip ended, go into standby + state.clear(STATE_WORKING); + return; + } +#else + dataInterval = dataIntervals[0]; +#endif + do { + long t = dataInterval - (millis() - startTime); + processBLE(t > 0 ? t : 0); + } while (millis() - startTime < dataInterval); +} + +bool initCell(bool quick = false) +{ + Serial.println("[CELL] Activating..."); + // power on network module + if (!teleClient.cell.begin(&sys)) { + Serial.println("[CELL] No supported module"); +#if ENABLE_OLED + oled.println("No Cell Module"); +#endif + return false; + } + if (quick) return true; +#if ENABLE_OLED + oled.print(teleClient.cell.deviceName()); + oled.println(" OK\r"); + oled.print("IMEI:"); + oled.println(teleClient.cell.IMEI); +#endif + Serial.print("CELL:"); + Serial.println(teleClient.cell.deviceName()); + if (!teleClient.cell.checkSIM(SIM_CARD_PIN)) { + Serial.println("NO SIM CARD"); + //return false; + } + Serial.print("IMEI:"); + Serial.println(teleClient.cell.IMEI); + Serial.println("[CELL] Searching..."); + if (*apn) { + Serial.print("APN:"); + Serial.println(apn); + } + if (teleClient.cell.setup(apn)) { + netop = teleClient.cell.getOperatorName(); + if (netop.length()) { + Serial.print("Operator:"); + Serial.println(netop); +#if ENABLE_OLED + oled.println(op); +#endif + } + +#if GNSS == GNSS_CELLULAR + if (teleClient.cell.setGPS(true)) { + Serial.println("CELL GNSS:OK"); + } +#endif + + ip = teleClient.cell.getIP(); + if (ip.length()) { + Serial.print("[CELL] IP:"); + Serial.println(ip); +#if ENABLE_OLED + oled.print("IP:"); + oled.println(ip); +#endif + } + state.set(STATE_CELL_CONNECTED); + } else { + char *p = strstr(teleClient.cell.getBuffer(), "+CPSI:"); + if (p) { + char *q = strchr(p, '\r'); + if (q) *q = 0; + Serial.print("[CELL] "); + Serial.println(p + 7); +#if ENABLE_OLED + oled.println(p + 7); +#endif + } else { + Serial.print(teleClient.cell.getBuffer()); + } + } + timeoutsNet = 0; + return state.check(STATE_CELL_CONNECTED); +} + +/******************************************************************************* + Initializing network, maintaining connection and doing transmissions +*******************************************************************************/ +void telemetry(void* inst) +{ + uint32_t lastRssiTime = 0; + uint8_t connErrors = 0; + CStorageRAM store; + store.init( +#if BOARD_HAS_PSRAM + (char*)heap_caps_malloc(SERIALIZE_BUFFER_SIZE, MALLOC_CAP_SPIRAM), +#else + (char*)malloc(SERIALIZE_BUFFER_SIZE), +#endif + SERIALIZE_BUFFER_SIZE + ); + teleClient.reset(); + + for (;;) { + if (state.check(STATE_STANDBY)) { + if (state.check(STATE_CELL_CONNECTED) || state.check(STATE_WIFI_CONNECTED)) { + teleClient.shutdown(); + netop = ""; + ip = ""; + rssi = 0; + } + state.clear(STATE_NET_READY | STATE_CELL_CONNECTED | STATE_WIFI_CONNECTED); + teleClient.reset(); + bufman.purge(); + + uint32_t t = millis(); + do { + delay(1000); + } while (state.check(STATE_STANDBY) && millis() - t < 1000L * PING_BACK_INTERVAL); + if (state.check(STATE_STANDBY)) { + // start ping +#if ENABLE_WIFI + if (wifiSSID[0]) { + Serial.print("[WIFI] Joining SSID:"); + Serial.println(wifiSSID); + teleClient.wifi.begin(wifiSSID, wifiPassword); + } + if (teleClient.wifi.setup()) { + Serial.println("[WIFI] Ping..."); + teleClient.ping(); + } + else +#endif + { + if (initCell()) { + Serial.println("[CELL] Ping..."); + teleClient.ping(); + } + } + teleClient.shutdown(); + state.clear(STATE_CELL_CONNECTED | STATE_WIFI_CONNECTED); + } + continue; + } + +#if ENABLE_WIFI + if (wifiSSID[0] && !state.check(STATE_WIFI_CONNECTED)) { + Serial.print("[WIFI] Joining SSID:"); + Serial.println(wifiSSID); + teleClient.wifi.begin(wifiSSID, wifiPassword); + teleClient.wifi.setup(); + } +#endif + + while (state.check(STATE_WORKING)) { +#if ENABLE_WIFI + if (wifiSSID[0]) { + if (!state.check(STATE_WIFI_CONNECTED) && teleClient.wifi.connected()) { + ip = teleClient.wifi.getIP(); + if (ip.length()) { + Serial.print("[WIFI] IP:"); + Serial.println(ip); + } + connErrors = 0; + if (teleClient.connect()) { + state.set(STATE_WIFI_CONNECTED | STATE_NET_READY); + beep(50); + // switch off cellular module when wifi connected + if (state.check(STATE_CELL_CONNECTED)) { + teleClient.cell.end(); + state.clear(STATE_CELL_CONNECTED); + Serial.println("[CELL] Deactivated"); + } + } + } else if (state.check(STATE_WIFI_CONNECTED) && !teleClient.wifi.connected()) { + Serial.println("[WIFI] Disconnected"); + state.clear(STATE_WIFI_CONNECTED); + } + } +#endif + if (!state.check(STATE_WIFI_CONNECTED) && !state.check(STATE_CELL_CONNECTED)) { + connErrors = 0; + if (!initCell() || !teleClient.connect()) { + teleClient.cell.end(); + state.clear(STATE_NET_READY | STATE_CELL_CONNECTED); + break; + } + Serial.println("[CELL] In service"); + state.set(STATE_NET_READY); + beep(50); + } + + if (millis() - lastRssiTime > SIGNAL_CHECK_INTERVAL * 1000) { +#if ENABLE_WIFI + if (state.check(STATE_WIFI_CONNECTED)) + { + rssi = teleClient.wifi.RSSI(); + } + else +#endif + { + rssi = teleClient.cell.RSSI(); + } + if (rssi) { + Serial.print("RSSI:"); + Serial.print(rssi); + Serial.println("dBm"); + } + lastRssiTime = millis(); + +#if ENABLE_WIFI + if (wifiSSID[0] && !state.check(STATE_WIFI_CONNECTED)) { + teleClient.wifi.begin(wifiSSID, wifiPassword); + } +#endif + } + + // get data from buffer + CBuffer* buffer = bufman.getNewest(); + if (!buffer) { + delay(50); + continue; + } +#if SERVER_PROTOCOL == PROTOCOL_UDP + store.header(devid); +#endif + store.timestamp(buffer->timestamp); + buffer->serialize(store); + bufman.free(buffer); + store.tailer(); + Serial.print("[DAT] "); + Serial.println(store.buffer()); + + // start transmission +#ifdef PIN_LED + if (ledMode == 0) digitalWrite(PIN_LED, HIGH); +#endif + + #if SERVER_ENCRYPTION_ENABLE == 1 + char *orig_send_buf = store.buffer(); + unsigned int orig_send_buf_len = store.length(); + // Increase the size of encrypted_buf to hold the nonce, the encrypted data, and the authentication tag. + unsigned char encrypted_buf[12 + orig_send_buf_len + 16]; // 12 bytes for nonce and 16 bytes for tag + encrypt_string((unsigned char *)orig_send_buf, orig_send_buf_len, encrypted_buf); + if (teleClient.transmit((const char *)encrypted_buf, sizeof(encrypted_buf))) { + #else + if (teleClient.transmit(store.buffer(), store.length())) { + #endif + // successfully sent + connErrors = 0; + showStats(); + } else { + timeoutsNet++; + connErrors++; + printTimeoutStats(); + if (connErrors < MAX_CONN_ERRORS_RECONNECT) { + // quick reconnect + teleClient.connect(true); + } + } +#ifdef PIN_LED + if (ledMode == 0) digitalWrite(PIN_LED, LOW); +#endif + store.purge(); + + teleClient.inbound(); + + if (state.check(STATE_CELL_CONNECTED) && !teleClient.cell.check(1000)) { + Serial.println("[CELL] Not in service"); + state.clear(STATE_NET_READY | STATE_CELL_CONNECTED); + break; + } + + if (syncInterval > 10000 && millis() - teleClient.lastSyncTime > syncInterval) { + Serial.println("[NET] Poor connection"); + timeoutsNet++; + if (!teleClient.connect()) { + connErrors++; + } + } + + if (connErrors >= MAX_CONN_ERRORS_RECONNECT) { +#if ENABLE_WIFI + if (state.check(STATE_WIFI_CONNECTED)) { + teleClient.wifi.end(); + state.clear(STATE_NET_READY | STATE_WIFI_CONNECTED); + break; + } +#endif + if (state.check(STATE_CELL_CONNECTED)) { + teleClient.cell.end(); + state.clear(STATE_NET_READY | STATE_CELL_CONNECTED); + break; + } + } + + if (deviceTemp >= COOLING_DOWN_TEMP) { + // device too hot, cool down by pause transmission + Serial.print("HIGH DEVICE TEMP: "); + Serial.println(deviceTemp); + bufman.purge(); + } + + } + } +} + +/******************************************************************************* + Implementing stand-by mode +*******************************************************************************/ +void standby() +{ + state.set(STATE_STANDBY); +#if STORAGE != STORAGE_NONE + if (state.check(STATE_STORAGE_READY)) { + logger.end(); + } +#endif + +#if !GNSS_ALWAYS_ON && GNSS == GNSS_STANDALONE + if (state.check(STATE_GPS_READY)) { + Serial.println("[GPS] OFF"); + sys.gpsEnd(true); + state.clear(STATE_GPS_READY); + gd = 0; + } +#endif + + state.clear(STATE_WORKING | STATE_OBD_READY | STATE_STORAGE_READY); + // this will put co-processor into sleep mode +#if ENABLE_OLED + oled.print("STANDBY"); + delay(1000); + oled.clear(); +#endif + Serial.println("STANDBY"); + obd.enterLowPowerMode(); +#if ENABLE_MEMS + calibrateMEMS(); + waitMotion(-1); +#elif ENABLE_OBD + do { + delay(5000); + } while (obd.getVoltage() < JUMPSTART_VOLTAGE); +#else + delay(5000); +#endif + Serial.println("WAKEUP"); + sys.resetLink(); +#if RESET_AFTER_WAKEUP +#if ENABLE_MEMS + if (mems) mems->end(); +#endif + ESP.restart(); +#endif + state.clear(STATE_STANDBY); +} + +/******************************************************************************* + Tasks to perform in idle/waiting time +*******************************************************************************/ +void genDeviceID(char* buf) +{ + uint64_t seed = ESP.getEfuseMac() >> 8; + for (int i = 0; i < 8; i++, seed >>= 5) { + byte x = (byte)seed & 0x1f; + if (x >= 10) { + x = x - 10 + 'A'; + switch (x) { + case 'B': x = 'W'; break; + case 'D': x = 'X'; break; + case 'I': x = 'Y'; break; + case 'O': x = 'Z'; break; + } + } else { + x += '0'; + } + buf[i] = x; + } + buf[8] = 0; +} + +void showSysInfo() +{ + Serial.print("CPU:"); + Serial.print(ESP.getCpuFreqMHz()); + Serial.print("MHz FLASH:"); + Serial.print(ESP.getFlashChipSize() >> 20); + Serial.println("MB"); + Serial.print("IRAM:"); + Serial.print(ESP.getHeapSize() >> 10); + Serial.print("KB"); +#if BOARD_HAS_PSRAM + if (psramInit()) { + Serial.print(" PSRAM:"); + Serial.print(esp_spiram_get_size() >> 20); + Serial.print("MB"); + } +#endif + Serial.println(); + + int rtc = rtc_clk_slow_freq_get(); + if (rtc) { + Serial.print("RTC:"); + Serial.println(rtc); + } + +#if ENABLE_OLED + oled.clear(); + oled.print("CPU:"); + oled.print(ESP.getCpuFreqMHz()); + oled.print("Mhz "); + oled.print(getFlashSize() >> 10); + oled.println("MB Flash"); +#endif + + Serial.print("DEVICE ID:"); + Serial.println(devid); +#if ENABLE_OLED + oled.print("DEVICE ID:"); + oled.println(devid); +#endif +} + +void loadConfig() +{ + size_t len; + len = sizeof(apn); + apn[0] = 0; + nvs_get_str(nvs, "CELL_APN", apn, &len); + if (!apn[0]) { + strcpy(apn, CELL_APN); + } + +#if ENABLE_WIFI + len = sizeof(wifiSSID); + nvs_get_str(nvs, "WIFI_SSID", wifiSSID, &len); + len = sizeof(wifiPassword); + nvs_get_str(nvs, "WIFI_PWD", wifiPassword, &len); +#endif +} + +void processBLE(int timeout) +{ +#if ENABLE_BLE + static byte echo = 0; + char* cmd; + if (!(cmd = ble_recv_command(timeout))) { + return; + } + + char *p = strchr(cmd, '\r'); + if (p) *p = 0; + char buf[48]; + int bufsize = sizeof(buf); + int n = 0; + if (echo) n += snprintf(buf + n, bufsize - n, "%s\r", cmd); + Serial.print("[BLE] "); + Serial.print(cmd); + if (!strcmp(cmd, "UPTIME") || !strcmp(cmd, "TICK")) { + n += snprintf(buf + n, bufsize - n, "%lu", millis()); + } else if (!strcmp(cmd, "BATT")) { + n += snprintf(buf + n, bufsize - n, "%.2f", (float)(analogRead(A0) * 42) / 4095); + } else if (!strcmp(cmd, "RESET")) { +#if STORAGE + logger.end(); +#endif + ESP.restart(); + // never reach here + } else if (!strcmp(cmd, "OFF")) { + state.set(STATE_STANDBY); + state.clear(STATE_WORKING); + n += snprintf(buf + n, bufsize - n, "OK"); + } else if (!strcmp(cmd, "ON")) { + state.clear(STATE_STANDBY); + n += snprintf(buf + n, bufsize - n, "OK"); + } else if (!strcmp(cmd, "ON?")) { + n += snprintf(buf + n, bufsize - n, "%u", state.check(STATE_STANDBY) ? 0 : 1); + } else if (!strcmp(cmd, "APN?")) { + n += snprintf(buf + n, bufsize - n, "%s", *apn ? apn : "DEFAULT"); + } else if (!strncmp(cmd, "APN=", 4)) { + n += snprintf(buf + n, bufsize - n, nvs_set_str(nvs, "CELL_APN", strcmp(cmd + 4, "DEFAULT") ? cmd + 4 : "") == ESP_OK ? "OK" : "ERR"); + loadConfig(); + } else if (!strcmp(cmd, "NET_OP")) { + if (state.check(STATE_WIFI_CONNECTED)) { +#if ENABLE_WIFI + n += snprintf(buf + n, bufsize - n, "%s", wifiSSID[0] ? wifiSSID : "-"); +#endif + } else { + snprintf(buf + n, bufsize - n, "%s", netop.length() ? netop.c_str() : "-"); + char *p = strchr(buf + n, ' '); + if (p) *p = 0; + n += strlen(buf + n); + } + } else if (!strcmp(cmd, "NET_IP")) { + n += snprintf(buf + n, bufsize - n, "%s", ip.length() ? ip.c_str() : "-"); + } else if (!strcmp(cmd, "NET_PACKET")) { + n += snprintf(buf + n, bufsize - n, "%u", teleClient.txCount); + } else if (!strcmp(cmd, "NET_DATA")) { + n += snprintf(buf + n, bufsize - n, "%u", teleClient.txBytes); + } else if (!strcmp(cmd, "NET_RATE")) { + n += snprintf(buf + n, bufsize - n, "%u", teleClient.startTime ? (unsigned int)((uint64_t)(teleClient.txBytes + teleClient.rxBytes) * 3600 / (millis() - teleClient.startTime)) : 0); + } else if (!strcmp(cmd, "RSSI")) { + n += snprintf(buf + n, bufsize - n, "%d", rssi); +#if ENABLE_WIFI + } else if (!strcmp(cmd, "SSID?")) { + n += snprintf(buf + n, bufsize - n, "%s", wifiSSID[0] ? wifiSSID : "-"); + } else if (!strncmp(cmd, "SSID=", 5)) { + const char* p = cmd + 5; + n += snprintf(buf + n, bufsize - n, nvs_set_str(nvs, "WIFI_SSID", strcmp(p, "-") ? p : "") == ESP_OK ? "OK" : "ERR"); + loadConfig(); + } else if (!strcmp(cmd, "WPWD?")) { + n += snprintf(buf + n, bufsize - n, "%s", wifiPassword[0] ? wifiPassword : "-"); + } else if (!strncmp(cmd, "WPWD=", 5)) { + const char* p = cmd + 5; + n += snprintf(buf + n, bufsize - n, nvs_set_str(nvs, "WIFI_PWD", strcmp(p, "-") ? p : "") == ESP_OK ? "OK" : "ERR"); + loadConfig(); +#else + } else if (!strcmp(cmd, "SSID?") || !strcmp(cmd, "WPWD?")) { + n += snprintf(buf + n, bufsize - n, "-"); +#endif +#if ENABLE_MEMS + } else if (!strcmp(cmd, "TEMP")) { + n += snprintf(buf + n, bufsize - n, "%d", (int)deviceTemp); + } else if (!strcmp(cmd, "ACC")) { + n += snprintf(buf + n, bufsize - n, "%.1f/%.1f/%.1f", acc[0], acc[1], acc[2]); + } else if (!strcmp(cmd, "GYRO")) { + n += snprintf(buf + n, bufsize - n, "%.1f/%.1f/%.1f", gyr[0], gyr[1], gyr[2]); + } else if (!strcmp(cmd, "GF")) { + n += snprintf(buf + n, bufsize - n, "%f", (float)sqrt(acc[0]*acc[0] + acc[1]*acc[1] + acc[2]*acc[2])); +#endif + } else if (!strcmp(cmd, "ATE0")) { + echo = 0; + n += snprintf(buf + n, bufsize - n, "OK"); + } else if (!strcmp(cmd, "ATE1")) { + echo = 1; + n += snprintf(buf + n, bufsize - n, "OK"); + } else if (!strcmp(cmd, "FS")) { + n += snprintf(buf + n, bufsize - n, "%u", +#if STORAGE == STORAGE_NONE + 0 +#else + logger.size() +#endif + ); + } else if (!memcmp(cmd, "01", 2)) { + byte pid = hex2uint8(cmd + 2); + for (byte i = 0; i < sizeof(obdData) / sizeof(obdData[0]); i++) { + if (obdData[i].pid == pid) { + n += snprintf(buf + n, bufsize - n, "%d", obdData[i].value); + pid = 0; + break; + } + } + if (pid) { + int value; + if (obd.readPID(pid, value)) { + n += snprintf(buf + n, bufsize - n, "%d", value); + } else { + n += snprintf(buf + n, bufsize - n, "N/A"); + } + } + } else if (!strcmp(cmd, "VIN")) { + n += snprintf(buf + n, bufsize - n, "%s", vin[0] ? vin : "N/A"); + } else if (!strcmp(cmd, "LAT") && gd) { + n += snprintf(buf + n, bufsize - n, "%f", gd->lat); + } else if (!strcmp(cmd, "LNG") && gd) { + n += snprintf(buf + n, bufsize - n, "%f", gd->lng); + } else if (!strcmp(cmd, "ALT") && gd) { + n += snprintf(buf + n, bufsize - n, "%d", (int)gd->alt); + } else if (!strcmp(cmd, "SAT") && gd) { + n += snprintf(buf + n, bufsize - n, "%u", (unsigned int)gd->sat); + } else if (!strcmp(cmd, "SPD") && gd) { + n += snprintf(buf + n, bufsize - n, "%d", (int)(gd->speed * 1852 / 1000)); + } else if (!strcmp(cmd, "CRS") && gd) { + n += snprintf(buf + n, bufsize - n, "%u", (unsigned int)gd->heading); + } else { + n += snprintf(buf + n, bufsize - n, "ERROR"); + } + Serial.print(" -> "); + Serial.println((p = strchr(buf, '\r')) ? p + 1 : buf); + if (n < bufsize - 1) { + buf[n++] = '\r'; + } else { + n = bufsize - 1; + } + buf[n] = 0; + ble_send_response(buf, n, cmd); +#else + if (timeout) delay(timeout); +#endif +} + +void setup() +{ + delay(500); + + // Initialize NVS + esp_err_t err = nvs_flash_init(); + if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { + // NVS partition was truncated and needs to be erased + // Retry nvs_flash_init + ESP_ERROR_CHECK(nvs_flash_erase()); + err = nvs_flash_init(); + } + ESP_ERROR_CHECK( err ); + err = nvs_open("storage", NVS_READWRITE, &nvs); + if (err == ESP_OK) { + loadConfig(); + } + +#if ENABLE_OLED + oled.begin(); + oled.setFontSize(FONT_SIZE_SMALL); +#endif + // initialize USB serial + Serial.begin(115200); + + // init LED pin +#ifdef PIN_LED + pinMode(PIN_LED, OUTPUT); + if (ledMode == 0) digitalWrite(PIN_LED, HIGH); +#endif + + // generate unique device ID + genDeviceID(devid); + +#if CONFIG_MODE_TIMEOUT + configMode(); +#endif + +#if LOG_EXT_SENSORS == 1 + pinMode(PIN_SENSOR1, INPUT); + pinMode(PIN_SENSOR2, INPUT); +#elif LOG_EXT_SENSORS == 2 + adc1_config_width(ADC_WIDTH_BIT_12); + adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11); + adc1_config_channel_atten(ADC1_CHANNEL_1, ADC_ATTEN_DB_11); +#endif + + // show system information + showSysInfo(); + + bufman.init(); + + //Serial.print(heap_caps_get_free_size(MALLOC_CAP_SPIRAM) >> 10); + //Serial.println("KB"); + +#if ENABLE_OBD + if (sys.begin()) { + Serial.print("TYPE:"); + Serial.println(sys.devType); + obd.begin(sys.link); + } +#else + sys.begin(false, true); +#endif + +#if ENABLE_MEMS +if (!state.check(STATE_MEMS_READY)) do { + Serial.print("MEMS:"); + mems = new ICM_42627; + byte ret = mems->begin(); + if (ret) { + state.set(STATE_MEMS_READY); + Serial.println("ICM-42627"); + break; + } + delete mems; + mems = new ICM_20948_I2C; + ret = mems->begin(); + if (ret) { + state.set(STATE_MEMS_READY); + Serial.println("ICM-20948"); + break; + } + delete mems; + /* + mems = new MPU9250; + ret = mems->begin(); + if (ret) { + state.set(STATE_MEMS_READY); + Serial.println("MPU-9250"); + break; + } + */ + mems = 0; + Serial.println("NO"); +} while (0); +#endif + +#if ENABLE_HTTPD + IPAddress ip; + if (serverSetup(ip)) { + Serial.println("HTTPD:"); + Serial.println(ip); +#if ENABLE_OLED + oled.println(ip); +#endif + } else { + Serial.println("HTTPD:NO"); + } +#endif + + state.set(STATE_WORKING); + +#if ENABLE_BLE + // init BLE + ble_init("FreematicsPlus"); +#endif + + // initialize components + initialize(); + + // initialize network and maintain connection + subtask.create(telemetry, "telemetry", 2, 8192); + +#ifdef PIN_LED + digitalWrite(PIN_LED, LOW); +#endif +} + +void loop() +{ + // error handling + if (!state.check(STATE_WORKING)) { + standby(); +#ifdef PIN_LED + if (ledMode == 0) digitalWrite(PIN_LED, HIGH); +#endif + initialize(); +#ifdef PIN_LED + digitalWrite(PIN_LED, LOW); +#endif + return; + } + + // collect and log data + process(); +} \ No newline at end of file diff --git a/esp32/telelogger/telemesh.cpp b/esp32/telelogger/telemesh.cpp new file mode 100644 index 0000000..fe852ae --- /dev/null +++ b/esp32/telelogger/telemesh.cpp @@ -0,0 +1,113 @@ +#include "FreematicsPlus.h" +#include "config.h" +#include "telemesh.h" +#if 0 +#include +#include + +static mdf_err_t event_loop_cb(mdf_event_loop_t event, void *ctx) +{ + switch (event) { + case MDF_EVENT_MWIFI_STARTED: + Serial.println("MESH is started"); + break; + + case MDF_EVENT_MWIFI_PARENT_CONNECTED: + Serial.println("Parent is connected on station interface"); + break; + + case MDF_EVENT_MWIFI_PARENT_DISCONNECTED: + Serial.println("Parent is disconnected on station interface"); + break; + + default: + break; + } + + return MDF_OK; +} + +static mdf_err_t wifi_init() +{ + mdf_err_t ret = nvs_flash_init(); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + nvs_flash_erase(); + ret = nvs_flash_init(); + } + + //MDF_ERROR_ASSERT(ret); + + tcpip_adapter_init(); + esp_event_loop_init(NULL, NULL); + esp_wifi_init(&cfg); + esp_wifi_set_storage(WIFI_STORAGE_FLASH); + esp_wifi_set_mode(WIFI_MODE_STA); + esp_wifi_set_ps(WIFI_PS_NONE); + esp_mesh_set_6m_rate(false); + return esp_wifi_start(); +} + +bool ClientWiFiMesh::begin(CFreematics* device) +{ + if (m_inited) return true; + + mwifi_init_config_t cfg = MWIFI_INIT_CONFIG_DEFAULT(); + mwifi_config_t config = {0}; + config.channel = WIFI_MESH_CHANNEL; + config.mesh_type = MWIFI_MESH_NODE; + memcpy(config.mesh_id, WIFI_MESH_ID, sizeof(WIFI_MESH_ID) - 1); + + mdf_event_loop_init(event_loop_cb); + wifi_init(); + mwifi_init(&cfg); + mwifi_set_config(&config); + if (mwifi_start() == MDF_OK) { + m_inited = true; + return true; + } + return false; +} + + bool ClientWiFiMesh::open(const char* host, uint16_t port) + { + return mwifi_is_connected(); + } + +bool ClientWiFiMesh::send(const char* data, unsigned int len) +{ + if (mwifi_is_connected()) { + mwifi_data_type_t data_type = {0x0}; + if (mwifi_write(NULL, &data_type, data, len, true) == MDF_OK) + return true; + } + return false; +} + +char* ClientWiFiMesh::receive(int* pbytes, unsigned int timeout) +{ + if (mwifi_is_connected()) { + uint8_t src_addr[MWIFI_ADDR_LEN] = {0x0}; + mwifi_data_type_t data_type = {0x0}; + size_t size = MESH_RECV_BUF_SIZE - 1; + if (mwifi_read(src_addr, &data_type, m_buffer, &size, timeout / portTICK_PERIOD_MS) == MDF_OK) { + m_buffer[size] = 0; + if (pbytes) *pbytes = size; + return m_buffer; + } + } + return 0; +} + +ClientWiFiMesh::ClientWiFiMesh() +{ + m_buffer = (char*)malloc(MESH_RECV_BUF_SIZE); +} + +ClientWiFiMesh::~ClientWiFiMesh() +{ + free(m_buffer); +} + +#endif \ No newline at end of file diff --git a/esp32/telelogger/telemesh.h b/esp32/telelogger/telemesh.h new file mode 100644 index 0000000..0746800 --- /dev/null +++ b/esp32/telelogger/telemesh.h @@ -0,0 +1,43 @@ +class ClientSerial +{ +public: + bool begin(CFreematics* device) { return true; } + void end() {} + bool open(const char* host, uint16_t port) { return true; } + void close() {} + bool send(const char* data, unsigned int len) + { + Serial.write((uint8_t*)data, len); + Serial.println(); + return true; + } + char* receive(int* pbytes = 0, unsigned int timeout = 5000) + { + Serial.setTimeout(timeout); + int bytes = Serial.readBytes((uint8_t*)m_buffer, sizeof(m_buffer) - 1); + if (pbytes) *pbytes = bytes; + return bytes > 0 ? m_buffer : 0; + } + const char* deviceName() { return "Serial"; } +private: + char m_buffer[128] = {0}; +}; + +#define MESH_RECV_BUF_SIZE 256 + +class ClientWiFiMesh +{ +public: + ClientWiFiMesh(); + ~ClientWiFiMesh(); + bool begin(CFreematics* device); + void end() {} + bool open(const char* host, uint16_t port); + void close() {} + bool send(const char* data, unsigned int len); + char* receive(int* pbytes = 0, unsigned int timeout = 5000); + const char* deviceName() { return "WiFi Mesh"; } +private: + char* m_buffer; + bool m_inited = false; +}; diff --git a/esp32/telelogger/telestore.cpp b/esp32/telelogger/telestore.cpp new file mode 100644 index 0000000..4eeec8c --- /dev/null +++ b/esp32/telelogger/telestore.cpp @@ -0,0 +1,263 @@ +#include +#include "telestore.h" + +void CStorage::log(uint16_t pid, uint8_t values[], uint8_t count) +{ + char buf[256]; + byte n = snprintf(buf, sizeof(buf), "%X%c%u", pid, m_delimiter, (unsigned int)values[0]); + for (byte m = 1; m < count; m++) { + n += snprintf(buf + n, sizeof(buf) - n, ";%u", (unsigned int)values[m]); + } + dispatch(buf, n); +} + +void CStorage::log(uint16_t pid, uint16_t values[], uint8_t count) +{ + char buf[256]; + byte n = snprintf(buf, sizeof(buf), "%X%c%u", pid, m_delimiter, (unsigned int)values[0]); + for (byte m = 1; m < count; m++) { + n += snprintf(buf + n, sizeof(buf) - n, ";%u", (unsigned int)values[m]); + } + dispatch(buf, n); +} + +void CStorage::log(uint16_t pid, uint32_t values[], uint8_t count) +{ + char buf[256]; + byte n = snprintf(buf, sizeof(buf), "%X%c%u", pid, m_delimiter, values[0]); + for (byte m = 1; m < count; m++) { + n += snprintf(buf + n, sizeof(buf) - n, ";%u", values[m]); + } + dispatch(buf, n); +} + +void CStorage::log(uint16_t pid, int32_t values[], uint8_t count) +{ + char buf[256]; + byte n = snprintf(buf, sizeof(buf), "%X%c%d", pid, m_delimiter, values[0]); + for (byte m = 1; m < count; m++) { + n += snprintf(buf + n, sizeof(buf) - n, ";%d", values[m]); + } + dispatch(buf, n); +} + +void CStorage::log(uint16_t pid, float values[], uint8_t count, const char* fmt) +{ + char buf[256]; + char *p = buf + snprintf(buf, sizeof(buf), "%X%c", pid, m_delimiter); + for (byte m = 0; m < count && (p - buf) < sizeof(buf) - 3; m++) { + if (m > 0) *(p++) = ';'; + int l = snprintf(p, sizeof(buf) - (p - buf), fmt, values[m]); + char *q = strchr(p, '.'); + if (q && atoi(q + 1) == 0) { + *q = 0; + if (*p == '-' && *(p + 1) == '0') { + *p = '0'; + *(++p) = 0; + } else { + p = q; + } + } else { + p += l; + } + } + dispatch(buf, (int)(p - buf)); +} + +void CStorage::timestamp(uint32_t ts) +{ + log(PID_TIMESTAMP, &ts, 1); +} + +void CStorage::dispatch(const char* buf, byte len) +{ + // output data via serial + Serial.write((uint8_t*)buf, len); + Serial.write(' '); + m_samples++; +} + +byte CStorage::checksum(const char* data, int len) +{ + byte sum = 0; + for (int i = 0; i < len; i++) sum += data[i]; + return sum; +} + +void CStorageRAM::dispatch(const char* buf, byte len) +{ + // reserve some space for checksum + int remain = m_cacheSize - m_cacheBytes - len - 3; + if (remain < 0) { + // m_cache full + return; + } + // store data in m_cache + memcpy(m_cache + m_cacheBytes, buf, len); + m_cacheBytes += len; + m_cache[m_cacheBytes++] = ','; + m_samples++; +} + +void CStorageRAM::header(const char* devid) +{ + m_cacheBytes = sprintf(m_cache, "%s#", devid); +} + +void CStorageRAM::tailer() +{ + if (m_cache[m_cacheBytes - 1] == ',') m_cacheBytes--; + m_cacheBytes += sprintf(m_cache + m_cacheBytes, "*%X", (unsigned int)checksum(m_cache, m_cacheBytes)); +} + +void CStorageRAM::untailer() +{ + char *p = strrchr(m_cache, '*'); + if (p) { + *p = ','; + m_cacheBytes = p + 1 - m_cache; + } +} + +void FileLogger::dispatch(const char* buf, byte len) +{ + if (m_id == 0) return; + + if (m_file.write((uint8_t*)buf, len) != len) { + // try again + if (m_file.write((uint8_t*)buf, len) != len) { + Serial.println("Error writing. End file logging."); + end(); + return; + } + } + m_file.write('\n'); + m_size += (len + 1); +} + +int FileLogger::getFileID(File& root) +{ + if (root) { + File file; + int id = 0; + while(file = root.openNextFile()) { + char *p = strrchr(file.name(), '/'); + unsigned int n = atoi(p ? p + 1 : file.name()); + if (n > id) id = n; + } + return id + 1; + } else { + return 0; + } +} + +bool SDLogger::init() +{ + SPI.begin(); + if (SD.begin(PIN_SD_CS, SPI, SPI_FREQ)) { + unsigned int total = SD.totalBytes() >> 20; + unsigned int used = SD.usedBytes() >> 20; + Serial.print("SD:"); + Serial.print(total); + Serial.print(" MB total, "); + Serial.print(used); + Serial.println(" MB used"); + return true; + } else { + Serial.println("NO SD CARD"); + return false; + } +} + +uint32_t SDLogger::begin() +{ + File root = SD.open("/DATA"); + m_id = getFileID(root); + if (m_id == 0) { + SD.mkdir("/DATA"); + m_id = 1; + } + char path[24]; + sprintf(path, "/DATA/%u.CSV", m_id); + Serial.print("File: "); + Serial.println(path); + m_file = SD.open(path, FILE_WRITE); + if (!m_file) { + Serial.println("File error"); + m_id = 0; + } + m_dataCount = 0; + return m_id; +} + +void SDLogger::flush() +{ + char path[24]; + sprintf(path, "/DATA/%u.CSV", m_id); + m_file.close(); + m_file = SD.open(path, FILE_APPEND); + if (!m_file) { + Serial.println("File error"); + } +} + +bool SPIFFSLogger::init() +{ + bool mounted = SPIFFS.begin(); + if (!mounted) { + Serial.println("Formatting SPIFFS..."); + mounted = SPIFFS.begin(true); + } + if (mounted) { + Serial.print("SPIFFS:"); + Serial.print(SPIFFS.totalBytes()); + Serial.print(" bytes total, "); + Serial.print(SPIFFS.usedBytes()); + Serial.println(" bytes used"); + } else { + Serial.println("No SPIFFS"); + } + return mounted; +} + +uint32_t SPIFFSLogger::begin() +{ + File root = SPIFFS.open("/"); + m_id = getFileID(root); + char path[24]; + sprintf(path, "/DATA/%u.CSV", m_id); + Serial.print("File: "); + Serial.println(path); + m_file = SPIFFS.open(path, FILE_WRITE); + if (!m_file) { + Serial.println("File error"); + m_id = 0; + } + m_dataCount = 0; + return m_id; +} + +void SPIFFSLogger::purge() +{ + // remove oldest file when unused space is insufficient + File root = SPIFFS.open("/"); + File file; + int idx = 0; + while(file = root.openNextFile()) { + if (!strncmp(file.name(), "/DATA/", 6)) { + unsigned int n = atoi(file.name() + 6); + if (n != 0 && (idx == 0 || n < idx)) idx = n; + } + } + if (idx) { + m_file.close(); + char path[32]; + sprintf(path, "/DATA/%u.CSV", idx); + SPIFFS.remove(path); + Serial.print(path); + Serial.println(" removed"); + sprintf(path, "/DATA/%u.CSV", m_id); + m_file = SPIFFS.open(path, FILE_APPEND); + if (!m_file) m_id = 0; + } +} diff --git a/esp32/telelogger/telestore.h b/esp32/telelogger/telestore.h new file mode 100644 index 0000000..5926b29 --- /dev/null +++ b/esp32/telelogger/telestore.h @@ -0,0 +1,94 @@ +#include +#include +#include +#include + +class CStorage; + +class CStorage { +public: + virtual bool init() { return true; } + virtual void uninit() {} + virtual void log(uint16_t pid, uint8_t values[], uint8_t count); + virtual void log(uint16_t pid, uint16_t values[], uint8_t count); + virtual void log(uint16_t pid, uint32_t values[], uint8_t count); + virtual void log(uint16_t pid, int32_t values[], uint8_t count); + virtual void log(uint16_t pid, float values[], uint8_t count, const char* fmt = "%f"); + virtual void timestamp(uint32_t ts); + virtual void purge() { m_samples = 0; } + virtual uint16_t samples() { return m_samples; } + virtual void dispatch(const char* buf, byte len); +protected: + byte checksum(const char* data, int len); + virtual void header(const char* devid) {} + virtual void tailer() {} + int m_samples = 0; + char m_delimiter = ':'; +}; + +class CStorageRAM: public CStorage { +public: + void init(char* cache, unsigned int cacheSize) + { + m_cacheSize = cacheSize; + m_cache = cache; + } + void uninit() + { + if (m_cache) { + delete m_cache; + m_cache = 0; + m_cacheSize = 0; + } + } + void purge() { m_cacheBytes = 0; m_samples = 0; } + unsigned int length() { return m_cacheBytes; } + char* buffer() { return m_cache; } + void dispatch(const char* buf, byte len); + void header(const char* devid); + void tailer(); + void untailer(); +protected: + unsigned int m_cacheSize = 0; + unsigned int m_cacheBytes = 0; + char* m_cache = 0; +}; + +class FileLogger : public CStorage { +public: + FileLogger() { m_delimiter = ','; } + virtual void dispatch(const char* buf, byte len); + virtual uint32_t size() { return m_size; } + virtual void end() + { + m_file.close(); + m_id = 0; + m_size = 0; + } + virtual void flush() + { + m_file.flush(); + } +protected: + int getFileID(File& root); + uint32_t m_dataTime = 0; + uint32_t m_dataCount = 0; + uint32_t m_size = 0; + uint32_t m_id = 0; + File m_file; +}; + +class SDLogger : public FileLogger { +public: + bool init(); + uint32_t begin(); + void flush(); +}; + +class SPIFFSLogger : public FileLogger { +public: + bool init(); + uint32_t begin(); +private: + void purge(); +};