├── SD ├── ble-oui.db ├── mac-oui.db └── mac-oui-light.db ├── .gitignore ├── screenshots ├── logo.png ├── capture1.png ├── capture2.png ├── capture3.png ├── logo-ble.png ├── BLECollector-Iconbar.jpg ├── BLECollector-Title.jpg ├── OdroidGo-fw-image.jpeg ├── capture-esp32-s3-box.jpg ├── BLECollector-HallOfMac.jpg ├── BLECollector-M5Stack.jpeg ├── BLECollector-TronRecognizer.jpg └── wrover-kit │ ├── BLECollector-2020-05-10_16h27m43s.jpg │ └── BLECollector-2020-05-10_16h28m52s.jpg ├── .gitmodules ├── LICENSE ├── ESP32-BLECollector ├── ESP32-BLECollector.ino ├── HID_XPad.h ├── SDUtils.h ├── DateTime.h ├── NTP.h ├── TimeUtils.h ├── GPS.h ├── RTC.h ├── ScrollPanel.h ├── Settings.h ├── BLEFileSharing.h ├── BLECache.h └── UI_Icons.h ├── platformio.ini ├── github.com.txt ├── .github └── workflows │ └── build.yml ├── phpsecu.re.txt ├── .travis.yml ├── README.md ├── log └── keywords.txt /SD/ble-oui.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/SD/ble-oui.db -------------------------------------------------------------------------------- /SD/mac-oui.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/SD/mac-oui.db -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | data-source 2 | BLEMenu.bin 3 | NTPMenu.bin 4 | blemacs.db 5 | build 6 | .pio 7 | -------------------------------------------------------------------------------- /SD/mac-oui-light.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/SD/mac-oui-light.db -------------------------------------------------------------------------------- /screenshots/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/screenshots/logo.png -------------------------------------------------------------------------------- /screenshots/capture1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/screenshots/capture1.png -------------------------------------------------------------------------------- /screenshots/capture2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/screenshots/capture2.png -------------------------------------------------------------------------------- /screenshots/capture3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/screenshots/capture3.png -------------------------------------------------------------------------------- /screenshots/logo-ble.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/screenshots/logo-ble.png -------------------------------------------------------------------------------- /screenshots/BLECollector-Iconbar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/screenshots/BLECollector-Iconbar.jpg -------------------------------------------------------------------------------- /screenshots/BLECollector-Title.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/screenshots/BLECollector-Title.jpg -------------------------------------------------------------------------------- /screenshots/OdroidGo-fw-image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/screenshots/OdroidGo-fw-image.jpeg -------------------------------------------------------------------------------- /screenshots/capture-esp32-s3-box.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/screenshots/capture-esp32-s3-box.jpg -------------------------------------------------------------------------------- /screenshots/BLECollector-HallOfMac.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/screenshots/BLECollector-HallOfMac.jpg -------------------------------------------------------------------------------- /screenshots/BLECollector-M5Stack.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/screenshots/BLECollector-M5Stack.jpeg -------------------------------------------------------------------------------- /screenshots/BLECollector-TronRecognizer.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/screenshots/BLECollector-TronRecognizer.jpg -------------------------------------------------------------------------------- /screenshots/wrover-kit/BLECollector-2020-05-10_16h27m43s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/screenshots/wrover-kit/BLECollector-2020-05-10_16h27m43s.jpg -------------------------------------------------------------------------------- /screenshots/wrover-kit/BLECollector-2020-05-10_16h28m52s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/HEAD/screenshots/wrover-kit/BLECollector-2020-05-10_16h28m52s.jpg -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tools/ESP32-BLEBeaconSpam"] 2 | path = tools/ESP32-BLEBeaconSpam 3 | url = https://github.com/tobozo/ESP32-BLEBeaconSpam 4 | [submodule "tools/ESP32-BLETimeServer"] 5 | path = tools/ESP32-BLETimeServer 6 | url = https://github.com/tobozo/ESP32-BLETimeServer 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 tobozo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ESP32-BLECollector/ESP32-BLECollector.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ESP32 BLE Collector - A BLE scanner with sqlite data persistence on the SD Card 4 | Source: https://github.com/tobozo/ESP32-BLECollector 5 | 6 | MIT License 7 | 8 | Copyright (c) 2018 tobozo 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | ----------------------------------------------------------------------------- 29 | 30 | Hardware requirements: 31 | - [mandatory] ESP32 (PSRam optional but recommended) 32 | - [mandatory] SD Card breakout (or bundled in Wrover-Kit, M5Stack, LoLinD32 Pro) 33 | - [mandatory] Micro SD (FAT32 formatted, max 32GB) 34 | - [mandatory] ST7789/ILI9341 320x240 TFT (or bundled in Wrover-Kit, Odroid-Go, M5Stack, LoLinD32 Pro) 35 | - [mandatory] 'mac-oui-light.db' and 'ble-oui.db' files copied on the Micro SD Card root (optional if you have another running BLECollector) 36 | - [optional] I2C RTC Module (see "#define HAS_EXTERNAL_RTC" in settings.h or display config) 37 | - [optional] Serial GPS Module (see "#define HAS_GPS" in settings.h or display config) 38 | 39 | Arduino IDE Settings: 40 | - Partition Scheme : Minimal SPIFFS (Large APPS with OTA) 41 | 42 | Optional I2C RTC Module requirements: 43 | - Flash the sketch to setup time in RTC 44 | 45 | Optional I2C+GPS: 46 | - Flash the sketch, wait for a GPS fix, then issue the "gpstime" command 47 | 48 | */ 49 | #pragma GCC optimize("O3") 50 | #include "Settings.h" 51 | 52 | void setup() 53 | { 54 | BLECollector.init(); 55 | } 56 | 57 | 58 | void loop() 59 | { 60 | vTaskSuspend(NULL); 61 | } 62 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | 2 | ; PlatformIO Project Configuration File 3 | ; 4 | ; Build options: build flags, source filter 5 | ; Upload options: custom upload port, speed and extra flags 6 | ; Library options: dependencies, extra library storages 7 | ; Advanced options: extra scripting 8 | ; 9 | ; Please visit documentation for the other options and examples 10 | ; http://docs.platformio.org/page/projectconf.html 11 | 12 | [platformio] 13 | src_dir = ESP32-BLECollector 14 | ;default_envs = m5stack-fire 15 | ;default_envs = m5stack-core-esp32 16 | default_envs = m5stack-core2 17 | ;default_envs = odroid_esp32 18 | 19 | [env] 20 | ;platform = espressif32@3.3.2 21 | ;platform = espressif32 22 | ;platform = https://github.com/platformio/platform-espressif32.git 23 | ;platform = https://github.com/platformio/platform-espressif32.git#feature/arduino-upstream 24 | ;platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#2.0.1-RC1 25 | platform = https://github.com/tasmota/platform-espressif32 26 | platform_packages = framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32/releases/download/2.0.9/esp32-2.0.9.zip 27 | framework = arduino 28 | upload_speed = 921600 29 | monitor_speed = 115200 30 | ;lib_ldf_mode = deep 31 | build_flags = 32 | -DCORE_DEBUG_LEVEL=2 33 | lib_deps = 34 | ; SPI 35 | SD 36 | LovyanGFX 37 | git+https://github.com/tobozo/ESP32-Chimera-Core#1.5.0 38 | git+https://github.com/tobozo/M5Stack-SD-Updater#1.2.8 39 | NimBLE-Arduino 40 | FS 41 | SPI 42 | Wire 43 | Time 44 | ;M5Stack-SD-Updater 45 | Sqlite3Esp32 46 | TinyGPSPlus 47 | 48 | 49 | [env:m5stack-fire] 50 | board = m5stack-fire 51 | board_build.partitions = default_16MB.csv 52 | upload_port = /dev/ttyACM0 53 | ;board_upload.flash_size=4MB 54 | ;board_build.f_flash = 80000000L 55 | 56 | ; board = esp32dev 57 | ; board_build.partitions = min_spiffs.csv 58 | ; board_upload.flash_size=4MB 59 | ; build_flags = 60 | ; -DCONFIG_APP_ROLLBACK_ENABLE=y 61 | ; -DBOARD_HAS_PSRAM 62 | ; -DARDUINO_M5STACK_FIRE 63 | ; -mfix-esp32-psram-cache-issue 64 | ; -mfix-esp32-psram-cache-strategy=memw 65 | 66 | lib_deps = 67 | ${env.lib_deps} 68 | FastLED@3.4.0 69 | 70 | [env:m5stack-core-esp32] 71 | board = m5stack-core-esp32 72 | debug_build_flags = -Os 73 | build_src_flags = 74 | -DWITHOUT_WIFI=1 75 | board_build.partitions = min_spiffs.csv 76 | lib_deps = 77 | ${env.lib_deps} 78 | lib_ignore = 79 | M5Stack 80 | 81 | [env:m5stack-core2] 82 | board = m5stack-core2 83 | board_build.partitions = default_16MB.csv 84 | lib_deps = 85 | ${env.lib_deps} 86 | lib_ignore = 87 | M5Core2 88 | 89 | [env:m5stack-cores3] 90 | board = esp32-s3-devkitc-1 91 | board_upload.flash_size = 16MB 92 | ;board_upload.maximum_size = 2097152 93 | ;board_upload.maximum_size = 3145728 94 | board_build.arduino.memory_type = qio_qspi 95 | build_flags = 96 | ${env.build_flags} 97 | -DARDUINO_M5STACK_CORES3 98 | -DBOARD_HAS_PSRAM 99 | -DARDUINO_UDB_MODE=1 100 | build_src_flags = 101 | -DWITHOUT_WIFI=1 102 | lib_deps = 103 | ${env.lib_deps} 104 | lib_ignore = 105 | M5Stack 106 | M5Core2 107 | M5CoreS3 108 | SD_MMC 109 | 110 | 111 | [env:odroid_esp32] 112 | board = odroid_esp32 113 | board_build.partitions = min_spiffs.csv 114 | build_src_flags = 115 | -DWITHOUT_WIFI=1 116 | lib_deps = 117 | ${env.lib_deps} 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /github.com.txt: -------------------------------------------------------------------------------- 1 | const char* github_com_ca =\ 2 | "-----BEGIN CERTIFICATE-----\n"\ 3 | "MIIFBjCCBK2gAwIBAgIQDovzdw2S0Zbwu2H5PEFmvjAKBggqhkjOPQQDAjBnMQsw\n"\ 4 | "CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xPzA9BgNVBAMTNkRp\n"\ 5 | "Z2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIFRMUyBIeWJyaWQgRUNDIFNIQTI1NiAyMDIw\n"\ 6 | "IENBMTAeFw0yMTAzMjUwMDAwMDBaFw0yMjAzMzAyMzU5NTlaMGYxCzAJBgNVBAYT\n"\ 7 | "AlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2Nv\n"\ 8 | "MRUwEwYDVQQKEwxHaXRIdWIsIEluYy4xEzARBgNVBAMTCmdpdGh1Yi5jb20wWTAT\n"\ 9 | "BgcqhkjOPQIBBggqhkjOPQMBBwNCAASt9vd1sdNJVApdEHG93CUGSyIcoiNOn6H+\n"\ 10 | "udCMvTm8DCPHz5GmkFrYRasDE77BI3q5xMidR/aW4Ll2a1A2ZvcNo4IDOjCCAzYw\n"\ 11 | "HwYDVR0jBBgwFoAUUGGmoNI1xBEqII0fD6xC8M0pz0swHQYDVR0OBBYEFCexfp+7\n"\ 12 | "JplQ2PPDU1v+MRawux5yMCUGA1UdEQQeMByCCmdpdGh1Yi5jb22CDnd3dy5naXRo\n"\ 13 | "dWIuY29tMA4GA1UdDwEB/wQEAwIHgDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB\n"\ 14 | "BQUHAwIwgbEGA1UdHwSBqTCBpjBRoE+gTYZLaHR0cDovL2NybDMuZGlnaWNlcnQu\n"\ 15 | "Y29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZVRMU0h5YnJpZEVDQ1NIQTI1NjIwMjBD\n"\ 16 | "QTEuY3JsMFGgT6BNhktodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vRGlnaUNlcnRI\n"\ 17 | "aWdoQXNzdXJhbmNlVExTSHlicmlkRUNDU0hBMjU2MjAyMENBMS5jcmwwPgYDVR0g\n"\ 18 | "BDcwNTAzBgZngQwBAgIwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2Vy\n"\ 19 | "dC5jb20vQ1BTMIGSBggrBgEFBQcBAQSBhTCBgjAkBggrBgEFBQcwAYYYaHR0cDov\n"\ 20 | "L29jc3AuZGlnaWNlcnQuY29tMFoGCCsGAQUFBzAChk5odHRwOi8vY2FjZXJ0cy5k\n"\ 21 | "aWdpY2VydC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlVExTSHlicmlkRUNDU0hB\n"\ 22 | "MjU2MjAyMENBMS5jcnQwDAYDVR0TAQH/BAIwADCCAQUGCisGAQQB1nkCBAIEgfYE\n"\ 23 | "gfMA8QB2ACl5vvCeOTkh8FZzn2Old+W+V32cYAr4+U1dJlwlXceEAAABeGq/vRoA\n"\ 24 | "AAQDAEcwRQIhAJ7miER//DRFnDJNn6uUhgau3WMt4vVfY5dGigulOdjXAiBIVCfR\n"\ 25 | "xjK1v4F31+sVaKzyyO7JAa0fzDQM7skQckSYWQB3ACJFRQdZVSRWlj+hL/H3bYbg\n"\ 26 | "IyZjrcBLf13Gg1xu4g8CAAABeGq/vTkAAAQDAEgwRgIhAJgAEkoJQRivBlwo7x67\n"\ 27 | "3oVsf1ip096WshZqmRCuL/JpAiEA3cX4rb3waLDLq4C48NSoUmcw56PwO/m2uwnQ\n"\ 28 | "prb+yh0wCgYIKoZIzj0EAwIDRwAwRAIgK+Kv7G+/KkWkNZg3PcQFp866Z7G6soxo\n"\ 29 | "a4etSZ+SRlYCIBSiXS20Wc+yjD111nPzvQUCfsP4+DKZ3K+2GKsERD6d\n"\ 30 | "-----END CERTIFICATE-----\n"\ 31 | "-----BEGIN CERTIFICATE-----\n"\ 32 | "MIIEGzCCAwOgAwIBAgIQBmcDW7sU/WOvwNaoU07+FjANBgkqhkiG9w0BAQsFADBs\n"\ 33 | "MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"\ 34 | "d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j\n"\ 35 | "ZSBFViBSb290IENBMB4XDTIwMTIxNzAwMDAwMFoXDTMwMTIxNjIzNTk1OVowZzEL\n"\ 36 | "MAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMT8wPQYDVQQDEzZE\n"\ 37 | "aWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBUTFMgSHlicmlkIEVDQyBTSEEyNTYgMjAy\n"\ 38 | "MCBDQTEwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARnvW/xPOudvtC252wTq9ef\n"\ 39 | "6fbdFeWPkOscfpRTkciuHj7UcumQSH3lzkPEIx0KpesWa8epsks7QwkZ4fU/Tkf9\n"\ 40 | "o4IBhzCCAYMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUUGGmoNI1xBEq\n"\ 41 | "II0fD6xC8M0pz0swHwYDVR0jBBgwFoAUsT7DaQP4v0cB1JgmGggC72NkK8MwDgYD\n"\ 42 | "VR0PAQH/BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjB/Bggr\n"\ 43 | "BgEFBQcBAQRzMHEwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv\n"\ 44 | "bTBJBggrBgEFBQcwAoY9aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD\n"\ 45 | "ZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNydDBLBgNVHR8ERDBCMECgPqA8hjpo\n"\ 46 | "dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZS\n"\ 47 | "b290Q0EuY3JsMDAGA1UdIAQpMCcwCAYGZ4EMAQICMAgGBmeBDAECAzAHBgVngQwB\n"\ 48 | "ATAIBgZngQwBAgEwDQYJKoZIhvcNAQELBQADggEBAHMQH8hhiBfNbxwEwxbbTAnu\n"\ 49 | "jPyUh/oi0JrfZI3u9JuiLqca720D6foS/AB5+4EIxpm7CMG4MdN/l7oAiDipaCPv\n"\ 50 | "mOmpYUpnT7A63Cr0q4g84rI1ZmdqA40lVUUf6qC6E34tC73qDQF8TJSrfscWFdCl\n"\ 51 | "RXR9J4QGrkZ2VNMSDzlDRzWCaA95MfO8x01l+ZdopdE8FvM78gGd4zxeWb8v991+\n"\ 52 | "mBxTDepqKuy/jF5Rm6Bhfxr33ADRs60s1t16dtZ3pOYLALBTPD5KhZ6a+/dk5dnh\n"\ 53 | "6c4PaeZQYBUAh+GuxfaBlU4qQ8EtjBMCQHreMIwXHYHW5FRYGjgR4NMuaIw2jD0=\n"\ 54 | "-----END CERTIFICATE-----\n"\ 55 | ""; 56 | -------------------------------------------------------------------------------- /ESP32-BLECollector/HID_XPad.h: -------------------------------------------------------------------------------- 1 | // https://www.tindie.com/products/deshipu/x-pad-buttons-shield-for-d1-mini-version-60/ 2 | #define XPAD_SDA 26 // pin number 3 | #define XPAD_SCL 27 // pin number 4 | #define XPAD_I2C_ADDR 0x10 5 | 6 | #define XPAD_DOWN 0x01 7 | #define XPAD_UP 0x02 8 | #define XPAD_RIGHT 0x04 9 | #define XPAD_LEFT 0x08 10 | #define XPAD_B 0x10 11 | #define XPAD_A 0x20 12 | #define XPAD_C 0x40 13 | #define XPAD_D 0x80 14 | 15 | 16 | struct XPadButton { 17 | const uint8_t bitValue; //expected bit returned from I2C when pushed 18 | uint8_t pressed = 0; 19 | XPadButton( uint8_t _bitValue ) : bitValue( _bitValue) { } 20 | uint8_t wasPressed() { 21 | return pressed; 22 | } 23 | }; 24 | 25 | 26 | class XPad { 27 | public: 28 | 29 | uint8_t state; //current button state 30 | 31 | XPadButton *BtnA = new XPadButton( XPAD_A ); 32 | XPadButton *BtnB = new XPadButton( XPAD_B ); 33 | XPadButton *BtnC = new XPadButton( XPAD_C ); 34 | XPadButton *BtnD = new XPadButton( XPAD_D ); 35 | XPadButton *BtnUP = new XPadButton( XPAD_UP ); 36 | XPadButton *BtnDOWN = new XPadButton( XPAD_DOWN ); 37 | XPadButton *BtnRIGHT = new XPadButton( XPAD_RIGHT ); 38 | XPadButton *BtnLEFT = new XPadButton( XPAD_LEFT ); 39 | 40 | void init() { 41 | Wire.begin(XPAD_SDA, XPAD_SCL); 42 | _time = millis(); 43 | _lastState = state; 44 | _changed = 0; 45 | _hold_time = -1; 46 | _lastTime = _time; 47 | _lastChange = _time; 48 | _pressTime = _time; 49 | _padVal = 0; 50 | } 51 | 52 | void setPads() { 53 | uint8_t pressed = wasPressed(); 54 | if( pressed ) { 55 | BtnA->pressed = pressed & BtnA->bitValue; 56 | BtnB->pressed = pressed & BtnB->bitValue; 57 | BtnC->pressed = pressed & BtnC->bitValue; 58 | BtnD->pressed = pressed & BtnD->bitValue; 59 | BtnUP->pressed = pressed & BtnUP->bitValue; 60 | BtnDOWN->pressed = pressed & BtnDOWN->bitValue; 61 | BtnRIGHT->pressed = pressed & BtnRIGHT->bitValue; 62 | BtnLEFT->pressed = pressed & BtnLEFT->bitValue; 63 | } else { 64 | BtnA->pressed = pressed; 65 | BtnB->pressed = pressed; 66 | BtnC->pressed = pressed; 67 | BtnD->pressed = pressed; 68 | BtnUP->pressed = pressed; 69 | BtnDOWN->pressed = pressed; 70 | BtnRIGHT->pressed = pressed; 71 | BtnLEFT->pressed = pressed; 72 | } 73 | } 74 | 75 | uint8_t update(void) { 76 | static uint32_t ms; 77 | 78 | ms = millis(); 79 | Wire.requestFrom(XPAD_I2C_ADDR, 1); 80 | _padVal = Wire.read(); 81 | 82 | if (ms - _lastChange < _dbTime) { 83 | _lastTime = _time; 84 | _time = ms; 85 | _changed = 0; 86 | } else { 87 | _lastTime = _time; 88 | _time = ms; 89 | _lastState = state; 90 | state = _padVal; 91 | if (state != _lastState) { 92 | _lastChange = ms; 93 | _changed = 1; 94 | if (state) { _pressTime = _time; } 95 | } else { 96 | _changed = 0; 97 | } 98 | } 99 | 100 | setPads(); 101 | return state; 102 | } 103 | 104 | uint8_t wasPressed(void) { 105 | return state && _changed; 106 | } 107 | 108 | 109 | private: 110 | uint8_t _padVal; //value returned by I2C (may contain multiple pushes) 111 | uint8_t _lastState; //previous button state 112 | uint8_t _changed; //state changed since last read 113 | uint32_t _time; //time of current state (all times are in ms) 114 | uint32_t _lastTime; //time of previous state 115 | uint32_t _lastChange; //time of last state change 116 | uint32_t _dbTime = 100; //debounce time 117 | uint32_t _pressTime; //press time 118 | uint32_t _hold_time; //hold time call wasreleasefor 119 | 120 | }; 121 | 122 | 123 | static XPad XPadShield; 124 | -------------------------------------------------------------------------------- /ESP32-BLECollector/SDUtils.h: -------------------------------------------------------------------------------- 1 | /*\ 2 | * SD Helpers 3 | \*/ 4 | 5 | 6 | #pragma GCC diagnostic ignored "-Wunused-variable" 7 | 8 | static bool sd_mounted = false; 9 | 10 | 11 | 12 | bool M5StackSDBegin() 13 | { 14 | #ifdef _SD_H_ 15 | return M5.sd_begin(); 16 | //return SD.begin(4, SPI, 20000000); 17 | #endif 18 | return false; 19 | } 20 | 21 | 22 | static const char* _FSFilePath( fs::File *file ) { 23 | #if defined ESP_IDF_VERSION_MAJOR && ESP_IDF_VERSION_MAJOR >= 4 24 | return file->path(); 25 | #else 26 | return file->name(); 27 | #endif 28 | } 29 | 30 | 31 | bool SDSetup() 32 | { 33 | if(sd_mounted) return true; 34 | unsigned long max_wait = 500; 35 | byte attempts = 100; 36 | while ( sd_mounted == false && attempts>0) { 37 | if ( SD_begin ) { 38 | sd_mounted = true; 39 | } else { 40 | log_e("[SD] Mount Failed"); 41 | delay(max_wait); 42 | if(attempts%2==0) { 43 | IconRender( &SDLoaderIcon, ICON_STATUS_disk00, (tft.width()-30)/2, 100 ); 44 | } else { 45 | IconRender( &SDLoaderIcon, ICON_STATUS_disk01, (tft.width()-30)/2, 100 ); 46 | } 47 | attempts--; 48 | } 49 | } 50 | if( attempts != 100 ) { 51 | //tft.fillRect( (tft.width()-30)/2, 100, 30, 30, Out.BgColor ); 52 | } 53 | return sd_mounted; 54 | } 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | static void listDirs(fs::FS &fs, const char * dirname, uint8_t levels, const char* needle=NULL) 63 | { 64 | Serial.printf("\nListing directories: %s\n\n", dirname); 65 | 66 | File root = fs.open(dirname); 67 | if(!root){ 68 | Serial.println("Failed to open directory"); 69 | return; 70 | } 71 | if(!root.isDirectory()){ 72 | Serial.println("Not a directory"); 73 | return; 74 | } 75 | 76 | File file = root.openNextFile(); 77 | Serial.println(" NAME | | SIZE"); 78 | Serial.println("-------------------------------------------------------------------------"); 79 | //unsigned long totalSize = 0; 80 | char fileDate[64] = "1980-01-01 00:07:20"; 81 | time_t lastWrite; 82 | struct tm * tmstruct; 83 | 84 | // show folders first 85 | while( file ) { 86 | lastWrite = file.getLastWrite(); 87 | tmstruct = localtime(&lastWrite); 88 | 89 | sprintf(fileDate, "%04d-%02d-%02d %02d:%02d:%02d",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec); 90 | if( (tmstruct->tm_year)+1900 < 2000 ) { 91 | // time is not set 92 | } 93 | if(file.isDirectory()) { 94 | Serial.printf( " %-32s | %20s | DIRECTORY\n", _FSFilePath( &file ), fileDate ); 95 | } 96 | file.close(); 97 | file = root.openNextFile(); 98 | } 99 | file.close(); 100 | root.close(); 101 | } 102 | 103 | 104 | static void listFiles(fs::FS &fs, const char * dirname, uint8_t levels, const char* needle=NULL) 105 | { 106 | //Serial.printf("\nListing Files: %s\n\n", dirname); 107 | 108 | File root = fs.open(dirname); 109 | if(!root){ 110 | Serial.println("Failed to open directory"); 111 | return; 112 | } 113 | if(!root.isDirectory()){ 114 | Serial.println("Not a directory"); 115 | return; 116 | } 117 | 118 | File file = root.openNextFile(); 119 | unsigned long totalSize = 0; 120 | char fileDate[64] = "1980-01-01 00:07:20"; 121 | time_t lastWrite; 122 | struct tm * tmstruct; 123 | 124 | // show files (TODO: sort by date) 125 | while( file ) { 126 | 127 | lastWrite = file.getLastWrite(); 128 | tmstruct = localtime(&lastWrite); 129 | 130 | sprintf(fileDate, "%04d-%02d-%02d %02d:%02d:%02d",(tmstruct->tm_year)+1900,( tmstruct->tm_mon)+1, tmstruct->tm_mday,tmstruct->tm_hour , tmstruct->tm_min, tmstruct->tm_sec); 131 | if( (tmstruct->tm_year)+1900 < 2000 ) { 132 | // time is not set 133 | } 134 | 135 | if(!file.isDirectory()) { 136 | const char* fileName = _FSFilePath ( &file ); 137 | unsigned long fileSize = file.size(); 138 | if( needle!=NULL && strcmp( fileName, needle ) == 0 ) { 139 | Serial.printf( "* %-32s | %20s | %8ld Bytes\n", fileName, fileDate, fileSize ); 140 | } else { 141 | Serial.printf( " %-32s | %20s | %8ld Bytes\n", fileName, fileDate, fileSize ); 142 | } 143 | totalSize += fileSize; 144 | }/* else { 145 | Serial.printf( " %-32s | %20s | DIRECTORY\n", file.name(), fileDate ); 146 | }*/ 147 | file.close(); 148 | file = root.openNextFile(); 149 | } 150 | file.close(); 151 | root.close(); 152 | 153 | Serial.printf("\nTotal space used: %ld Bytes\n\n", totalSize); 154 | } 155 | 156 | 157 | static void listDir(fs::FS &fs, const char * dirname, uint8_t levels, const char* needle=NULL) 158 | { 159 | listDirs( fs, dirname, levels, needle ); 160 | listFiles( fs, dirname, levels, needle ); 161 | } 162 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | env: 4 | SKETCH_NAME: "*.ino" 5 | PROJECT_NAME: ESP32-BLECollector 6 | 7 | on: 8 | push: 9 | paths: 10 | - '**.ino' 11 | - '**.cpp' 12 | - '**.hpp' 13 | - '**.h' 14 | - '**.c' 15 | - '**build.yml' 16 | pull_request: 17 | release: 18 | types: [published] 19 | 20 | workflow_dispatch: 21 | inputs: 22 | # logLevel: 23 | # description: 'Log level' 24 | # required: true 25 | # default: 'warning' 26 | tag_name: 27 | description: 'Target tag' 28 | 29 | 30 | jobs: 31 | 32 | matrix_build: 33 | 34 | name: ${{ matrix.fancy-name }} 35 | 36 | runs-on: ubuntu-latest 37 | 38 | strategy: 39 | matrix: 40 | 41 | arduino-boards-fqbn: 42 | - esp32:esp32:m5stack-core-esp32:PartitionScheme=min_spiffs 43 | - esp32:esp32:m5stack-fire 44 | - esp32:esp32:m5stack-core2 45 | - esp32:esp32:odroid_esp32:PartitionScheme=min_spiffs 46 | - esp32:esp32:esp32s3box 47 | 48 | platform-url: 49 | - https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json 50 | 51 | required-libraries: 52 | - ESP32-Chimera-Core,Time,LovyanGFX,M5Stack-SD-Updater,Sqlite3Esp32,TinyGPSPlus,NimBLE-Arduino 53 | 54 | include: 55 | - arduino-boards-fqbn: esp32:esp32:m5stack-core-esp32:PartitionScheme=min_spiffs 56 | fancy-name: M5Stack 57 | bin-name: M5stack-BLECollector.bin 58 | - arduino-boards-fqbn: esp32:esp32:m5stack-core2 59 | fancy-name: M5Core2 60 | bin-name: M5Core2-BLECollector.bin 61 | - arduino-boards-fqbn: esp32:esp32:m5stack-fire 62 | fancy-name: M5Fire 63 | bin-name: M5Fire-BLECollector.bin 64 | - arduino-boards-fqbn: esp32:esp32:odroid_esp32:PartitionScheme=min_spiffs 65 | fancy-name: OdroidGo 66 | bin-name: OdroidGo-BLECollector.bin 67 | - arduino-boards-fqbn: esp32:esp32:esp32s3box 68 | fancy-name: S3Box 69 | bin-name: S3Box-BLECollector.bin 70 | 71 | fail-fast: false 72 | 73 | steps: 74 | - name: Checkout 75 | uses: actions/checkout@v3 76 | with: 77 | ref: ${{ github.event.pull_request.head.sha }} 78 | 79 | - name: Compile example 80 | uses: ArminJo/arduino-test-compile@v3 81 | with: 82 | arduino-board-fqbn: ${{ matrix.arduino-boards-fqbn }} 83 | platform-url: ${{ matrix.platform-url }} 84 | required-libraries: ${{ matrix.required-libraries }} 85 | build-properties: ${{ toJson(matrix.build-properties) }} 86 | sketch-names: ${{ env.SKETCH_NAME }} 87 | set-build-path: true 88 | extra-arduino-cli-args: --warnings none 89 | 90 | - name: Copy compiled binary 91 | #if: startsWith(github.ref, 'refs/tags/') 92 | run: | 93 | binFile=`find . | grep 'ESP32-BLECollector.ino.bin' | head -1` 94 | binPath=`dirname $binFile` 95 | echo "binPath: $binPath" 96 | echo "binFile: $binFile" 97 | echo "binName: ${{ matrix.bin-name }}" 98 | ls $binPath 99 | mkdir -p /home/runner/builds 100 | cp "$binFile" "/home/runner/builds/${{ matrix.bin-name }}" 101 | 102 | - name: Upload artifact ${{ matrix.bin-name }} 103 | uses: actions/upload-artifact@v3 104 | #if: startsWith(github.ref, 'refs/tags/') 105 | with: 106 | name: ${{ matrix.bin-name }} 107 | path: /home/runner/builds/${{ matrix.bin-name }} 108 | 109 | post_build: 110 | 111 | name: Gather Artefacts 112 | 113 | runs-on: ubuntu-latest 114 | 115 | # wait until matrix jobs are all finished 116 | needs: matrix_build 117 | 118 | steps: 119 | - name: Checkout 120 | uses: actions/checkout@v3 121 | 122 | - name: Create artifacts dir 123 | #if: startsWith(github.ref, 'refs/tags/') 124 | run: mkdir -p /home/runner/builds 125 | 126 | - name: Download artifacts 127 | uses: actions/download-artifact@v4.1.7 128 | #if: startsWith(github.ref, 'refs/tags/') 129 | with: 130 | path: /home/runner/builds 131 | 132 | - name: Dispatch check 133 | uses: softprops/action-gh-release@v1 134 | if: github.event.inputs.tag_name != '' 135 | with: 136 | tag_name: ${{ github.event.inputs.tag_name }} 137 | files: | 138 | /home/runner/builds/M5stack-BLECollector.bin/* 139 | /home/runner/builds/M5Core2-BLECollector.bin/* 140 | /home/runner/builds/M5Fire-BLECollector.bin/* 141 | /home/runner/builds/OdroidGo-BLECollector.bin/* 142 | /home/runner/builds/S3Box-BLECollector.bin/* 143 | 144 | - name: Release check 145 | uses: softprops/action-gh-release@v1 146 | if: startsWith(github.ref, 'refs/tags/') 147 | with: 148 | files: | 149 | /home/runner/builds/M5stack-BLECollector.bin 150 | /home/runner/builds/M5Core2-BLECollector.bin 151 | /home/runner/builds/M5Fire-BLECollector.bin 152 | /home/runner/builds/OdroidGo-BLECollector.bin 153 | /home/runner/builds/S3Box-BLECollector.bin 154 | -------------------------------------------------------------------------------- /ESP32-BLECollector/DateTime.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ESP32 DateTime wrapper for the BLE Collector 4 | MIT License 5 | 6 | Copyright (c) 2018 tobozo 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | 26 | ----------------------------------------------------------------------------- 27 | 28 | This wrapper mainly exists to supply friend methods to the RTC Library based on JeeLabs's code http://news.jeelabs.org/code/ 29 | The changes are added dependencies to PaulStoffregen's Time library https://github.com/PaulStoffregen/Time/ 30 | 31 | */ 32 | 33 | #include // needed by BLETimeServer 34 | #include // https://github.com/PaulStoffregen/Time 35 | 36 | // helper 37 | static uint8_t DateTimeConv2d(const char* p) 38 | { 39 | uint8_t v = 0; 40 | if ('0' <= *p && *p <= '9') 41 | v = *p - '0'; 42 | return 10 * v + *++p - '0'; 43 | } 44 | 45 | static bool TimeIsSet = false; 46 | 47 | // Simple general-purpose date/time class (no TZ / DST / leap second handling!) 48 | class DateTime 49 | { 50 | public: 51 | DateTime( uint32_t t=0 ); 52 | DateTime( tmElements_t dateTimeNow ); 53 | DateTime( uint16_t year, uint8_t month, uint8_t day, 54 | uint8_t hour=0, uint8_t min=0, uint8_t sec=0 ); 55 | DateTime( const char* date, const char* time ); 56 | uint16_t year() const { return 1970 + yOff; } 57 | uint8_t month() const { return m; } 58 | uint8_t day() const { return d; } 59 | uint8_t hour() const { return hh; } 60 | uint8_t minute() const { return mm; } 61 | uint8_t second() const { return ss; } 62 | tmElements_t get_tm() const { return tm; } 63 | uint32_t unixtime() const; // 32-bit times as seconds since 1/1/1970 64 | static uint32_t tm2unixtime(tmElements_t tm); // conversion utility 65 | protected: 66 | uint8_t yOff=0, m=0, d=0, hh=0, mm=0, ss=0; 67 | tmElements_t tm; 68 | }; 69 | 70 | DateTime::DateTime(uint32_t unixtime) 71 | { 72 | breakTime(unixtime, tm); 73 | m = tm.Month; 74 | d = tm.Day; 75 | hh = tm.Hour; 76 | mm = tm.Minute; 77 | ss = tm.Second; 78 | yOff = tm.Year; // offset from 1970; 79 | }; 80 | DateTime::DateTime(tmElements_t dateTimeNow) 81 | { 82 | tm = dateTimeNow; 83 | m = tm.Month; 84 | d = tm.Day; 85 | hh = tm.Hour; 86 | mm = tm.Minute; 87 | ss = tm.Second; 88 | yOff = tm.Year; // offset from 1970; 89 | }; 90 | DateTime::DateTime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t min, uint8_t sec) 91 | { 92 | if (year >= 1970) 93 | year -= 1970; // year to offset 94 | yOff = year; 95 | m = month; 96 | d = day; 97 | hh = hour; 98 | mm = min; 99 | ss = sec; 100 | tm = {ss, mm, hh, 0, d, m, yOff}; 101 | }; 102 | DateTime::DateTime (const char* date, const char* time) 103 | { 104 | // A convenient constructor for using "the compiler's time": 105 | // DateTime now (__DATE__, __TIME__); 106 | // sample input: date = "Dec 26 2009", time = "12:34:56" 107 | yOff = DateTimeConv2d(date + 9) + 30; // 2000 offset to 1970 offset 108 | // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec 109 | switch (date[0]) { 110 | case 'J': m = (date[1] == 'a') ? 1 : (date[2] == 'n') ? 6 : 7; break; 111 | case 'F': m = 2; break; 112 | case 'A': m = date[2] == 'r' ? 4 : 8; break; 113 | case 'M': m = date[2] == 'r' ? 3 : 5; break; 114 | case 'S': m = 9; break; 115 | case 'O': m = 10; break; 116 | case 'N': m = 11; break; 117 | case 'D': m = 12; break; 118 | default: m = 0; 119 | } 120 | d = DateTimeConv2d(date + 4); 121 | hh = DateTimeConv2d(time); 122 | mm = DateTimeConv2d(time + 3); 123 | ss = DateTimeConv2d(time + 6); 124 | tm = {ss, mm, hh, 0, d, m, yOff}; 125 | }; 126 | uint32_t DateTime::unixtime() const 127 | { 128 | return tm2unixtime( tm ); 129 | } 130 | uint32_t DateTime::tm2unixtime(tmElements_t tm_) 131 | { 132 | uint32_t unixtime = makeTime(tm_); // convert time elements into time_t 133 | return unixtime; 134 | } 135 | 136 | 137 | 138 | // for debug 139 | 140 | void dumpTime(const char* message, DateTime dateTime) 141 | { 142 | Serial.printf("[%s]: %04d-%02d-%02d %02d:%02d:%02d\n", 143 | message, 144 | dateTime.year(), 145 | dateTime.month(), 146 | dateTime.day(), 147 | dateTime.hour(), 148 | dateTime.minute(), 149 | dateTime.second() 150 | ); 151 | } 152 | 153 | void dumpTime(const char* message, tmElements_t tm) 154 | { 155 | Serial.printf("[%s]: %04d-%02d-%02d (WDay:%d) %02d:%02d:%02d\n", 156 | message, 157 | tm.Year + 1970, 158 | tm.Month, 159 | tm.Day, 160 | tm.Wday, 161 | tm.Hour, 162 | tm.Minute, 163 | tm.Second 164 | ); 165 | } 166 | void dumpTime(const char* message, struct tm *info) 167 | { 168 | Serial.printf("%s (GMT%s%.2g [%s]): %04d-%02d-%02d %02d:%02d:%02d\n", 169 | message, 170 | timeZone>0 ? "+" : "", 171 | timeZone, 172 | summerTime ? "CEST" : "CET", 173 | info->tm_year + 1900, 174 | info->tm_mon+1, 175 | info->tm_mday, 176 | info->tm_hour, 177 | info->tm_min, 178 | info->tm_sec 179 | ); 180 | } 181 | 182 | void dumpTime(const char* message, time_t epoch) 183 | { 184 | tmElements_t nowUnixDateTime; 185 | breakTime( epoch, nowUnixDateTime ); 186 | dumpTime( message, nowUnixDateTime ); 187 | } 188 | -------------------------------------------------------------------------------- /phpsecu.re.txt: -------------------------------------------------------------------------------- 1 | const char* phpsecu_re_ca =\ 2 | "-----BEGIN CERTIFICATE-----\n"\ 3 | "MIIFaDCCBFCgAwIBAgISBDl8eb4rN/D82OiEejAPHmegMA0GCSqGSIb3DQEBCwUA\n"\ 4 | "MDIxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQD\n"\ 5 | "EwJSMzAeFw0yMTEwMDEyMjExMTVaFw0yMTEyMzAyMjExMTRaMBkxFzAVBgNVBAMT\n"\ 6 | "DnBocHNlY3VyZS5pbmZvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\n"\ 7 | "02OVckSpVIONFSJDOtb1738TAFYR6z1N/0kG4ja9QDN62K4lseLGDv1dqEPOgXJz\n"\ 8 | "cXF1IJBW0VzFS+IYVkP8zm1/j0Auk2jxxZp5qeHhOANSeluh7mVn+V3UXU+Jvz4h\n"\ 9 | "7lTraHDunS47yQc8qFMA5BG99KX8sGylWvP1H4RW4Q0tIZE386RJ4zI9rTyqjHS6\n"\ 10 | "q3TgyGLyezQRpR67mXXyVo0XZU9NRwasfTm3YDYoknYRaXb7aTLUlT8gLqxWHjjH\n"\ 11 | "mmD5Xyo9SPv94H0AjsY+vU3Rt4v41XQSgS3LK8SoFxT1RQp8cp0a7/cC5vaV8uM0\n"\ 12 | "qkSsLKLCgMi2EwwFj42yDQIDAQABo4ICjzCCAoswDgYDVR0PAQH/BAQDAgWgMB0G\n"\ 13 | "A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1Ud\n"\ 14 | "DgQWBBT6Eu6fG0Eeunq0JNzQDcGnM4aZ9DAfBgNVHSMEGDAWgBQULrMXt1hWy65Q\n"\ 15 | "CUDmH6+dixTCxjBVBggrBgEFBQcBAQRJMEcwIQYIKwYBBQUHMAGGFWh0dHA6Ly9y\n"\ 16 | "My5vLmxlbmNyLm9yZzAiBggrBgEFBQcwAoYWaHR0cDovL3IzLmkubGVuY3Iub3Jn\n"\ 17 | "LzBeBgNVHREEVzBVghNtYWlsLnBocHNlY3VyZS5pbmZvggpwaHBzZWN1LnJlgg5w\n"\ 18 | "aHBzZWN1cmUuaW5mb4IOd3d3LnBocHNlY3UucmWCEnd3dy5waHBzZWN1cmUuaW5m\n"\ 19 | "bzBMBgNVHSAERTBDMAgGBmeBDAECATA3BgsrBgEEAYLfEwEBATAoMCYGCCsGAQUF\n"\ 20 | "BwIBFhpodHRwOi8vY3BzLmxldHNlbmNyeXB0Lm9yZzCCAQUGCisGAQQB1nkCBAIE\n"\ 21 | "gfYEgfMA8QB2AESUZS6w7s6vxEAH2Kj+KMDa5oK+2MsxtT/TM5a1toGoAAABfD4g\n"\ 22 | "SjcAAAQDAEcwRQIgDu1O+44+cl5bjWrvdxl5hCkvnJK0EJnaeatvac2n4j8CIQDI\n"\ 23 | "qant8PLkXPV1dVkEBmnFDnyxCcsxVSN8TyRBBneqiQB3AH0+8viP/4hVaCTCwMqe\n"\ 24 | "Uol5K8UOeAl/LmqXaJl+IvDXAAABfD4gSlkAAAQDAEgwRgIhAOBZ/a/lW6/KhGon\n"\ 25 | "YXaMO/WuKg2Ped4BQQjPstlrzYk9AiEA8ut0aPVCQHEwMPg5IUBC8USrthRlWfPK\n"\ 26 | "jeHUGR6y1DQwDQYJKoZIhvcNAQELBQADggEBAF6JMXJK8GwVua9DV70XXp/FHPUY\n"\ 27 | "aLrO9nlFGI1DwlQDt4xy/I9Az5DFp5flPc5RC8QNC4B7LgQvJnvI/rTbcrQt02KU\n"\ 28 | "lnRPAhogm1UuL/yQgz+RyhE9BYrtRG0GP0GbxUO5guEwBZPT9tlsWlUpesvicXTG\n"\ 29 | "OZw0aRdC5OQ95gGAxOL+DKiUWEqWMM2B+BuyUwQ2hLsV52IXc3QOmWXTeuuZdtmn\n"\ 30 | "pNTibYGQH/Ngm9GMCxVfYaORFlbl95cCu4E8XOX1EYI5aGP/UuC90Iuz/EnjuGsd\n"\ 31 | "5UbgCjMfSqdRL9/zTjur/ydEYjGMeGe8MJhX+NmQShd7cTc0zWobgEUXt7c=\n"\ 32 | "-----END CERTIFICATE-----\n"\ 33 | "-----BEGIN CERTIFICATE-----\n"\ 34 | "MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw\n"\ 35 | "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"\ 36 | "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw\n"\ 37 | "WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n"\ 38 | "RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n"\ 39 | "AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP\n"\ 40 | "R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx\n"\ 41 | "sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm\n"\ 42 | "NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg\n"\ 43 | "Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG\n"\ 44 | "/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC\n"\ 45 | "AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB\n"\ 46 | "Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA\n"\ 47 | "FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw\n"\ 48 | "AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw\n"\ 49 | "Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB\n"\ 50 | "gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W\n"\ 51 | "PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl\n"\ 52 | "ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz\n"\ 53 | "CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm\n"\ 54 | "lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4\n"\ 55 | "avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2\n"\ 56 | "yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O\n"\ 57 | "yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids\n"\ 58 | "hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+\n"\ 59 | "HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv\n"\ 60 | "MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX\n"\ 61 | "nLRbwHOoq7hHwg==\n"\ 62 | "-----END CERTIFICATE-----\n"\ 63 | "-----BEGIN CERTIFICATE-----\n"\ 64 | "MIIFYDCCBEigAwIBAgIQQAF3ITfU6UK47naqPGQKtzANBgkqhkiG9w0BAQsFADA/\n"\ 65 | "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n"\ 66 | "DkRTVCBSb290IENBIFgzMB4XDTIxMDEyMDE5MTQwM1oXDTI0MDkzMDE4MTQwM1ow\n"\ 67 | "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"\ 68 | "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwggIiMA0GCSqGSIb3DQEB\n"\ 69 | "AQUAA4ICDwAwggIKAoICAQCt6CRz9BQ385ueK1coHIe+3LffOJCMbjzmV6B493XC\n"\ 70 | "ov71am72AE8o295ohmxEk7axY/0UEmu/H9LqMZshftEzPLpI9d1537O4/xLxIZpL\n"\ 71 | "wYqGcWlKZmZsj348cL+tKSIG8+TA5oCu4kuPt5l+lAOf00eXfJlII1PoOK5PCm+D\n"\ 72 | "LtFJV4yAdLbaL9A4jXsDcCEbdfIwPPqPrt3aY6vrFk/CjhFLfs8L6P+1dy70sntK\n"\ 73 | "4EwSJQxwjQMpoOFTJOwT2e4ZvxCzSow/iaNhUd6shweU9GNx7C7ib1uYgeGJXDR5\n"\ 74 | "bHbvO5BieebbpJovJsXQEOEO3tkQjhb7t/eo98flAgeYjzYIlefiN5YNNnWe+w5y\n"\ 75 | "sR2bvAP5SQXYgd0FtCrWQemsAXaVCg/Y39W9Eh81LygXbNKYwagJZHduRze6zqxZ\n"\ 76 | "Xmidf3LWicUGQSk+WT7dJvUkyRGnWqNMQB9GoZm1pzpRboY7nn1ypxIFeFntPlF4\n"\ 77 | "FQsDj43QLwWyPntKHEtzBRL8xurgUBN8Q5N0s8p0544fAQjQMNRbcTa0B7rBMDBc\n"\ 78 | "SLeCO5imfWCKoqMpgsy6vYMEG6KDA0Gh1gXxG8K28Kh8hjtGqEgqiNx2mna/H2ql\n"\ 79 | "PRmP6zjzZN7IKw0KKP/32+IVQtQi0Cdd4Xn+GOdwiK1O5tmLOsbdJ1Fu/7xk9TND\n"\ 80 | "TwIDAQABo4IBRjCCAUIwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYw\n"\ 81 | "SwYIKwYBBQUHAQEEPzA9MDsGCCsGAQUFBzAChi9odHRwOi8vYXBwcy5pZGVudHJ1\n"\ 82 | "c3QuY29tL3Jvb3RzL2RzdHJvb3RjYXgzLnA3YzAfBgNVHSMEGDAWgBTEp7Gkeyxx\n"\ 83 | "+tvhS5B1/8QVYIWJEDBUBgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEB\n"\ 84 | "ATAwMC4GCCsGAQUFBwIBFiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQu\n"\ 85 | "b3JnMDwGA1UdHwQ1MDMwMaAvoC2GK2h0dHA6Ly9jcmwuaWRlbnRydXN0LmNvbS9E\n"\ 86 | "U1RST09UQ0FYM0NSTC5jcmwwHQYDVR0OBBYEFHm0WeZ7tuXkAXOACIjIGlj26Ztu\n"\ 87 | "MA0GCSqGSIb3DQEBCwUAA4IBAQAKcwBslm7/DlLQrt2M51oGrS+o44+/yQoDFVDC\n"\ 88 | "5WxCu2+b9LRPwkSICHXM6webFGJueN7sJ7o5XPWioW5WlHAQU7G75K/QosMrAdSW\n"\ 89 | "9MUgNTP52GE24HGNtLi1qoJFlcDyqSMo59ahy2cI2qBDLKobkx/J3vWraV0T9VuG\n"\ 90 | "WCLKTVXkcGdtwlfFRjlBz4pYg1htmf5X6DYO8A4jqv2Il9DjXA6USbW1FzXSLr9O\n"\ 91 | "he8Y4IWS6wY7bCkjCWDcRQJMEhg76fsO3txE+FiYruq9RUWhiF1myv4Q6W+CyBFC\n"\ 92 | "Dfvp7OOGAN6dEOM4+qR9sdjoSYKEBpsr6GtPAQw4dy753ec5\n"\ 93 | "-----END CERTIFICATE-----\n"\ 94 | ""; 95 | -------------------------------------------------------------------------------- /ESP32-BLECollector/NTP.h: -------------------------------------------------------------------------------- 1 | #pragma GCC diagnostic ignored "-Wunused-variable" 2 | 3 | /*\ 4 | * NTP Helpers 5 | \*/ 6 | 7 | // returns true if time has been updated 8 | static bool checkForTimeUpdate( DateTime &internalDateTime ) 9 | { 10 | //DateTime externalDateTime = internalDateTime; 11 | int64_t seconds_since_last_ntp_update = abs( int64_t(internalDateTime.unixtime() - lastSyncDateTime.unixtime()) ); 12 | if ( seconds_since_last_ntp_update >= 3500 ) { // GPS sync every hour +/- 3% precision 13 | log_d("seconds_since_last_ntp_update = now(%d) - last(%d) = %d seconds", internalDateTime.unixtime(), lastSyncDateTime.unixtime(), seconds_since_last_ntp_update); 14 | #if TIME_UPDATE_SOURCE==TIME_UPDATE_BLE // will trigger bletime if any BLETimeServer is found 15 | ForceBleTime = true; 16 | HasBTTime = false; 17 | return false; 18 | #elif HAS_GPS==true && TIME_UPDATE_SOURCE==TIME_UPDATE_GPS 19 | return setGPSTime(); 20 | #else 21 | #if HAS_EXTERNAL_RTC // adjust internal RTC accordingly, needed for filesystem operations 22 | DateTime externalDateTime = RTC.now(); // this may return some shit when I2C fails 23 | setTime( externalDateTime.unixtime() ); 24 | return true; 25 | #else 26 | // no reliable time source to update from 27 | return false; 28 | #endif 29 | #endif 30 | } else { 31 | // no need to update time 32 | return false; 33 | } 34 | } 35 | 36 | 37 | void TimeInit() 38 | { 39 | preferences.begin("BLEClock", true); 40 | lastSyncDateTime = preferences.getUInt("epoch", millis()); 41 | byte clockUpdateSource = preferences.getUChar("source", 0); 42 | preferences.end(); 43 | sprintf(YYYYMMDD_HHMMSS_Str, YYYYMMDD_HHMMSS_Tpl, 44 | lastSyncDateTime.year(), 45 | lastSyncDateTime.month(), 46 | lastSyncDateTime.day(), 47 | lastSyncDateTime.hour(), 48 | lastSyncDateTime.minute(), 49 | lastSyncDateTime.second() 50 | ); 51 | log_e("Defrosted lastSyncDateTime from NVS (may be bogus) : %s - source : %d", YYYYMMDD_HHMMSS_Str, clockUpdateSource); 52 | #if HAS_EXTERNAL_RTC 53 | if(clockUpdateSource==SOURCE_NONE) { 54 | if(RTC.isrunning()) { 55 | log_w("[RTC] Forcing source to RTC and rebooting"); 56 | logTimeActivity(SOURCE_RTC, 0); 57 | ESP.restart(); 58 | } else { 59 | log_e("[RTC] isn't running!"); 60 | return; 61 | } 62 | } 63 | nowDateTime = RTC.now(); // fetch time from external RTC 64 | setTime( nowDateTime.unixtime() ); // sync to local clock 65 | timeval epoch = {(time_t)nowDateTime.unixtime(), 0}; 66 | const timeval *tv = &epoch; 67 | settimeofday(tv, NULL); 68 | struct tm now; 69 | if( getLocalTime(&now,0) ) { 70 | dumpTime("System RTC adjusted from External RTC", nowDateTime); 71 | Serial.printf("[TZ] timeZone=%.2g, [%s]\n", timeZone, summerTime?"CEST":"CET"); 72 | } else { 73 | #ifdef WITH_WIFI 74 | log_e("System RTC setTime from External RTC failed, run stopBLE command to sync from NTP"); 75 | #endif 76 | dumpTime("RTC.now()", nowDateTime); 77 | } 78 | TimeIsSet = true; 79 | #endif 80 | } 81 | 82 | 83 | 84 | 85 | 86 | #ifdef WITH_WIFI 87 | 88 | 89 | #include 90 | #include "lwip/apps/sntp.h" 91 | 92 | // don't edit this, use "setPoolZone" serial command instead 93 | const char* DEFAULT_NTP_SERVER = "europe"; // will have ".pool.ntp.org" appended later 94 | static char NTP_SERVER[32]; // will hold the server Address from defaults or preferences 95 | 96 | static const struct 97 | { 98 | const char code[16]; 99 | const char *name; 100 | } ntpPoolZones[] = { 101 | { "africa", "Africa" }, 102 | { "antarctica", "Antarctica" }, 103 | { "asia", "Asia" }, 104 | { "europe", "Europe" }, 105 | { "north-america", "North America" }, 106 | { "oceania", "Oceania" }, 107 | { "south-america", "South America" }, 108 | { "", "" } 109 | }; 110 | 111 | 112 | static int getPoolZoneID( const char* code ) 113 | { 114 | size_t zones_len = sizeof ntpPoolZones / sizeof ntpPoolZones[0]; 115 | if( zones_len > 0 ) { 116 | for( int i=0;i -1 ) { 133 | sprintf( NTP_SERVER, poolZoneTpl, ntpPoolZones[poolZoneID].code ); 134 | } else { 135 | sprintf( NTP_SERVER, poolZoneTpl, DEFAULT_NTP_SERVER ); 136 | } 137 | Serial.printf("NTP Server set to : %s\n", NTP_SERVER ); 138 | } 139 | 140 | 141 | //const char* NTP_SERVER = "europe.pool.ntp.org"; 142 | //static bool getNTPTime(void); 143 | //static void initNTP(void); 144 | 145 | static void initNTP(void) 146 | { 147 | Serial.println("Initializing SNTP"); 148 | 149 | preferences.begin("BLEClock", true); 150 | String poolZone = preferences.getString( "poolZone", String(DEFAULT_NTP_SERVER) ); 151 | preferences.end(); 152 | 153 | setPoolZone( poolZone.c_str() ); 154 | sntp_setoperatingmode(SNTP_OPMODE_POLL); 155 | sntp_setservername(0, NTP_SERVER); 156 | sntp_init(); 157 | } 158 | 159 | 160 | static bool getNTPTime(void) 161 | { 162 | initNTP(); 163 | time_t now = 0; 164 | struct tm timeinfo = {}; 165 | int retry = 0; 166 | const int retry_count = 10; 167 | while(timeinfo.tm_year < (2016 - 1900) && ++retry < retry_count) { 168 | Serial.printf("Waiting for system time to be set... (%d/%d)\n", retry, retry_count); 169 | vTaskDelay(2000 / portTICK_PERIOD_MS); 170 | time(&now); 171 | localtime_r(&now, &timeinfo); 172 | } 173 | if( retry == retry_count ) { 174 | Serial.println("Failed to set system time from NTP..."); 175 | return false; 176 | } else { 177 | Serial.println("[NTP] System (Local time) adjusted!"); 178 | 179 | struct timeval tv; 180 | //bt_time_t _time; 181 | struct tm* _t; 182 | gettimeofday(&tv, nullptr); 183 | _t = localtime(&(tv.tv_sec)); 184 | DateTime NTP_UTC_Time( _t->tm_year-70/*1900 to 1970 offset*/, _t->tm_mon + 1, _t->tm_mday, _t->tm_hour, _t->tm_min, _t->tm_sec ); 185 | DateTime NTP_Local_Time( NTP_UTC_Time.unixtime() + (int(timeZone*100)*36) + (summerTime ? 3600 : 0) ); 186 | 187 | dumpTime("UTC Time provided by NTP", NTP_UTC_Time ); 188 | Serial.printf("[TZ] Applying timeZone (%.2g) [%s]\n", timeZone, summerTime?"CEST":"CET"); 189 | dumpTime("Local Time speculated from NTP", NTP_Local_Time ); 190 | #if HAS_EXTERNAL_RTC 191 | RTC.adjust( NTP_Local_Time ); 192 | Serial.println(""); 193 | dumpTime("RTC (Local time) adjusted from NTP. RTC.now()=", RTC.now() ); 194 | #endif 195 | nowDateTime = NTP_Local_Time; 196 | return true; 197 | } 198 | } 199 | 200 | 201 | 202 | 203 | #endif // WITH_WIFI 204 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3.6 3 | 4 | 5 | sudo: required 6 | 7 | env: 8 | global: 9 | # The Arduino IDE will be installed at APPLICATION_FOLDER/arduino 10 | - APPLICATION_FOLDER="${HOME}/arduino-ide" 11 | - SKETCHBOOK_FOLDER="${HOME}/arduino-sketchbook" 12 | 13 | before_install: 14 | 15 | # TODO: undo 16 | # remove submodules, we don't want those to be actually tested for compliance 17 | - git submodule status | rm -Rf `cut -d ' ' -f 3` 18 | 19 | # Formatting checks: 20 | # Check for files starting with a blank line 21 | #- find . -type d \( -path './.git' -o -path './examples' -o -path './src/Fonts' \) -prune -or -type f -print0 | xargs -0 -L1 bash -c 'head -1 "$0" | grep --binary-files=without-match --regexp="^$"; if [[ "$?" == "0" ]]; then echo "Blank line found at start of $0."; false; fi' 22 | # don't check for tabs 23 | #- find . -type d \( -path './.git' -o -path './examples' -o -path './src/Fonts' \) -prune -or -type f \( ! -iname ".gitmodules" \) -exec grep --with-filename --line-number --binary-files=without-match --regexp=$'\t' '{}' \; -exec echo 'Tab found.' \; -exec false '{}' + 24 | # Check for trailing whitespace 25 | #- find . -type d \( -path './.git' -o -path './examples' -o -path './src/Fonts' \) -prune -or -type f -exec grep --with-filename --line-number --binary-files=without-match --regexp='[[:blank:]]$' '{}' \; -exec echo 'Trailing whitespace found.' \; -exec false '{}' + 26 | # Check for non-Unix line endings 27 | #- find . -type d \( -path './.git' -o -path './examples' -o -path './src/Fonts' \) -prune -or -type f -exec grep --files-with-matches --binary-files=without-match --regexp=$'\r$' '{}' \; -exec echo 'Non-Unix EOL detected.' \; -exec false '{}' + 28 | # Check for blank lines at end of files 29 | #- find . -type d \( -path './.git' -o -path './examples' -o -path './src/Fonts' \) -prune -or -type f -print0 | xargs -0 -L1 bash -c 'tail -1 "$0" | grep --binary-files=without-match --regexp="^$"; if [[ "$?" == "0" ]]; then echo "Blank line found at end of $0."; false; fi' 30 | # Check for files that don't end in a newline (https://stackoverflow.com/a/25686825) 31 | #- find . -type d \( -path './.git' -o -path './examples' -o -path './src/Fonts' \) -prune -or -type f -print0 | xargs -0 -L1 bash -c 'if test "$(grep --files-with-matches --binary-files=without-match --max-count=1 --regexp='.*' "$0")" && test "$(tail --bytes=1 "$0")"; then echo "No new line at end of $0."; false; fi' 32 | 33 | - git clone https://github.com/per1234/arduino-ci-script.git "${HOME}/scripts/arduino-ci-script" 34 | - cd "${HOME}/scripts/arduino-ci-script" 35 | # Get new tags from the remote 36 | - git fetch --tags 37 | # Checkout the latest tag 38 | - git checkout $(git describe --tags `git rev-list --tags --max-count=1`) 39 | - source "${HOME}/scripts/arduino-ci-script/arduino-ci-script.sh" 40 | 41 | #- set_script_verbosity 1 42 | #- set_verbose_output_during_compilation "true" 43 | 44 | # Check for library issues that don't affect compilation 45 | - set_library_testing "true" 46 | 47 | - set_application_folder "$APPLICATION_FOLDER" 48 | - set_sketchbook_folder "$SKETCHBOOK_FOLDER" 49 | 50 | #- install_ide '("1.8.0" "1.8.10" "1.8.11")' 51 | - install_ide '("newest")' 52 | 53 | # Install the library from the repository 54 | - install_library "ESP32-Chimera-Core" 55 | #- install_library https://github.com/tobozo/ESP32-Chimera-Core/archive/Touch.zip 56 | - install_library https://github.com/PaulStoffregen/Time/archive/master.zip 57 | - install_library "LovyanGFX" 58 | - install_library "M5Stack-SD-Updater" 59 | - install_library Sqlite3Esp32 60 | - install_library https://github.com/mikalhart/TinyGPSPlus/archive/master.zip 61 | - install_library "NimBLE-Arduino" 62 | #- install_library https://github.com/tobozo/ESP32-BLECollector/releases/download/1.3/BLE.zip 63 | #- install_library https://github.com/tobozo/NimBLE-Arduino/archive/master.zip 64 | - install_package "esp32:esp32" "https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_dev_index.json" 65 | - pip install pyserial 66 | 67 | script: 68 | # Compile all example sketches included with the library 69 | # build_sketch arguments: sketch name, fqbn, allow failure, IDE version/list/range 70 | - check_library_manager_compliance "$TRAVIS_BUILD_DIR" 71 | # compile at least one basic example 72 | - set_ide_preference "compiler.warning_level=auto" 73 | 74 | - mkdir "/tmp/BLECollector-binaries" 75 | - mkdir "/tmp/m5stack" 76 | - mkdir "/tmp/odroidgo" 77 | - mkdir "/tmp/m5fire" 78 | - mkdir "/tmp/m5core2" 79 | 80 | #- set_ide_preference "custom_PartitionScheme=min_spiffs" 81 | - set_ide_preference "build.path=/tmp/m5stack" 82 | - build_sketch "${TRAVIS_BUILD_DIR}/ESP32-BLECollector.ino" "esp32:esp32:m5stack-core-esp32:PartitionScheme=min_spiffs" "false" "newest" 83 | - cp "/tmp/m5stack/ESP32-BLECollector.ino.bin" "/tmp/BLECollector-binaries/M5stack-BLECollector.bin" 84 | - set_ide_preference "build.path=/tmp/odroidgo" 85 | - build_sketch "${TRAVIS_BUILD_DIR}/ESP32-BLECollector.ino" "esp32:esp32:odroid_esp32:PartitionScheme=min_spiffs" "false" "newest" 86 | - cp "/tmp/odroidgo/ESP32-BLECollector.ino.bin" "/tmp/BLECollector-binaries/OdroidGo-BLECollector.bin" 87 | - set_ide_preference "build.path=/tmp/m5fire" 88 | - build_sketch "${TRAVIS_BUILD_DIR}/ESP32-BLECollector.ino" "esp32:esp32:m5stack-fire:PartitionScheme=default" "false" "newest" 89 | - cp "/tmp/m5fire/ESP32-BLECollector.ino.bin" "/tmp/BLECollector-binaries/M5Fire-BLECollector.bin" 90 | - set_ide_preference "build.path=/tmp/m5core2" 91 | - build_sketch "${TRAVIS_BUILD_DIR}/ESP32-BLECollector.ino" "esp32:esp32:m5stack-core2:PartitionScheme=default" "false" "newest" 92 | - cp "/tmp/m5core2/ESP32-BLECollector.ino.bin" "/tmp/BLECollector-binaries/M5Core2-BLECollector.bin" 93 | - ls /tmp/BLECollector-binaries -la 94 | 95 | #- if [ "$TRAVIS_BRANCH" != "master" ]; then echo "This commit was made against the $TRAVIS_BRANCH, skipping examples compilation"; exit 0; fi 96 | #- if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then echo "This is a pull request, skipping examples compilation"; exit 0; fi 97 | 98 | after_script: 99 | # Commit a report of the job results to the CI-reports repository 100 | #- USER_NAME="$(echo "$TRAVIS_REPO_SLUG" | cut -d'/' -f 1)" 101 | #- REPOSITORY_NAME="$(echo "$TRAVIS_REPO_SLUG" | cut -d'/' -f 2)" 102 | #- publish_report_to_repository "$REPORT_GITHUB_TOKEN" "https://github.com/${USER_NAME}/CI-reports.git" "$REPOSITORY_NAME" "build_$(printf "%05d\n" "${TRAVIS_BUILD_NUMBER}")" "false" 103 | # Print a tab separated report of all sketch verification results to the log 104 | - display_report 105 | 106 | notifications: 107 | email: 108 | on_success: always 109 | on_failure: always 110 | webhooks: 111 | urls: 112 | - https://www.travisbuddy.com/ 113 | on_success: never 114 | on_failure: always 115 | 116 | deploy: 117 | - provider: releases 118 | api_key: "${GH_TOKEN}" 119 | file: 120 | - "/tmp/BLECollector-binaries/M5stack-BLECollector.bin" 121 | - "/tmp/BLECollector-binaries/OdroidGo-BLECollector.bin" 122 | - "/tmp/BLECollector-binaries/M5Fire-BLECollector.bin" 123 | - "/tmp/BLECollector-binaries/M5Core2-BLECollector.bin" 124 | skip_cleanup: true 125 | draft: true 126 | on: 127 | branch: master 128 | -------------------------------------------------------------------------------- /ESP32-BLECollector/TimeUtils.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ESP32 BLE Collector - A BLE scanner with sqlite data persistence on the SD Card 4 | Source: https://github.com/tobozo/ESP32-BLECollector 5 | 6 | MIT License 7 | 8 | Copyright (c) 2018 tobozo 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | ----------------------------------------------------------------------------- 29 | 30 | */ 31 | 32 | #pragma GCC diagnostic ignored "-Wunused-variable" 33 | 34 | static unsigned long forcedUptime = 0; 35 | 36 | enum TimeUpdateSources 37 | { 38 | SOURCE_NONE = 0, 39 | SOURCE_COMPILER = 1, 40 | SOURCE_RTC = 2, 41 | SOURCE_NTP = 3, 42 | SOURCE_BLE = 4, 43 | SOURCE_GPS = 5 44 | }; 45 | 46 | void logTimeActivity(TimeUpdateSources source, int epoch) 47 | { 48 | preferences.begin("BLEClock", false); 49 | preferences.clear(); 50 | //DateTime epoch = RTC.now(); 51 | preferences.putUInt("epoch", epoch); 52 | preferences.putUChar("source", source); 53 | preferences.putFloat("timeZone", timeZone); 54 | preferences.putBool("summerTime", summerTime); 55 | preferences.end(); 56 | } 57 | 58 | void resetTimeActivity(TimeUpdateSources source) 59 | { 60 | preferences.begin("BLEClock", false); 61 | preferences.clear(); 62 | preferences.putUInt("epoch", 0); 63 | preferences.putUChar("source", source); 64 | preferences.putFloat("timeZone", timeZone); 65 | preferences.putBool("summerTime", summerTime); 66 | preferences.end(); 67 | } 68 | 69 | struct TimeActivity 70 | { 71 | DateTime epoch; 72 | byte source; 73 | }; 74 | 75 | /* 76 | * Time Update Situations 77 | * 78 | * External RTC update sources: BLE NTP, WiFi NTP 79 | * Internal RTC update sources: BLE NTP or External RTC 80 | * 81 | * External RTC needs update: 82 | * - when time is not set 83 | * - every hour 84 | * 85 | * Internal RTC needs update: 86 | * - when time is not set 87 | * - every hour 88 | * 89 | * 90 | * External RTC | No External RTC 91 | * -------------|---------------- 92 | * WiFi NTP X | Not applicable/useless 93 | * BLE NTP X | X 94 | * Internal RTC X | 95 | * GPS Module X | X 96 | * 97 | * */ 98 | 99 | #if HAS_GPS 100 | #include "GPS.h" 101 | #else 102 | __attribute__((unused)) static bool GPSHasFix = false; 103 | __attribute__((unused)) static bool GPSHasDateTime = false; 104 | #endif 105 | #include "NTP.h" 106 | 107 | 108 | void uptimeSet() 109 | { 110 | unsigned long seconds_since_boot = millis() / 1000; 111 | unsigned long minutes_since_boot = seconds_since_boot / 60; 112 | unsigned long mm = minutes_since_boot % 60; 113 | unsigned long hh = minutes_since_boot / 60; 114 | unsigned long forcedUptimes = forcedUptime + hh; 115 | if( forcedUptimes < 24 ) { 116 | sprintf( UpTimeString, UpTimeStringTpl, forcedUptimes, mm ); 117 | } else if( forcedUptimes <= 48 ) { 118 | sprintf( UpTimeString, UpTimeStringTplDays, forcedUptimes, "hours" ); 119 | } else { 120 | sprintf( UpTimeString, UpTimeStringTplDays, forcedUptimes / 24, "days" ); 121 | } 122 | } 123 | 124 | 125 | static void timeHousekeeping() 126 | { 127 | unsigned long seconds_since_boot = millis() / 1000; 128 | unsigned long minutes_since_boot = seconds_since_boot / 60; 129 | __attribute__((unused)) unsigned long mm = minutes_since_boot % 60; 130 | __attribute__((unused)) unsigned long hh = minutes_since_boot / 60; 131 | __attribute__((unused)) unsigned long ss = seconds_since_boot % 60; 132 | 133 | DateTime internalDateTime = DateTime(year(), month(), day(), hour(), minute(), second()); 134 | // before adjustment checks 135 | if( current_hour != internalDateTime.hour() ) { 136 | if( current_hour != -1 ) { 137 | log_e("hourchangeTrigger=true (%02dh => %02dh)", current_hour, internalDateTime.hour()); 138 | HourChangeTrigger = true; 139 | } 140 | current_hour = internalDateTime.hour(); 141 | } 142 | 143 | if( current_day != internalDateTime.day() ) { 144 | if( current_day != -1 ) { 145 | // day changed! update bool so DB.maintain() and BLE know what to do 146 | log_e("DayChangeTrigger=true (%02d => %02d)", current_day, internalDateTime.day()); 147 | DayChangeTrigger = true; 148 | HourChangeTrigger = false; 149 | } 150 | current_day = internalDateTime.day(); 151 | } 152 | 153 | // - get the time from the internal clock 154 | // - compare with external clocks sources if applicable 155 | if( HourChangeTrigger || DayChangeTrigger ) { 156 | if( checkForTimeUpdate( internalDateTime ) ) { // and update internal clock if necessary 157 | internalDateTime = DateTime(year(), month(), day(), hour(), minute(), second()); 158 | current_hour = internalDateTime.hour(); 159 | } 160 | } 161 | sprintf(hhmmString, hhmmStringTpl, internalDateTime.hour(), internalDateTime.minute()); 162 | 163 | #if HAS_EXTERNAL_RTC 164 | 165 | if( abs( int64_t(seconds_since_boot - internalDateTime.unixtime()) ) > 2 ) { // internal datetime is set 166 | // safe to assume internal RTC is running 167 | TimeIsSet = true; 168 | } 169 | sprintf(hhmmssString, hhmmssStringTpl, hh, mm, ss); 170 | 171 | #else // HAS_EXTERNAL_RTC=false 172 | 173 | sprintf(hhmmssString, hhmmssStringTpl, internalDateTime.hour(), internalDateTime.minute(), internalDateTime.second()); 174 | 175 | #endif // HAS_EXTERNAL_RTC 176 | 177 | nowDateTime = internalDateTime; 178 | uptimeSet(); 179 | log_v("Time: %s, Uptime: %s", hhmmString, UpTimeString ); 180 | } 181 | 182 | 183 | 184 | bool RTCSetup() 185 | { 186 | #if HAS_EXTERNAL_RTC 187 | RTC.begin( RTC_SDA, RTC_SCL ); 188 | delay(100); 189 | if (!RTC.isrunning()) { // first run use case (or dead RTC battery) 190 | log_e("[RTC (SDA:%d, SCL:%d)] NOT running, will adjust from compilation DateTime", RTC_SDA, RTC_SCL); 191 | RTC.adjust(DateTime(__DATE__, __TIME__)); 192 | logTimeActivity(SOURCE_COMPILER, DateTime(__DATE__, __TIME__).unixtime() ); 193 | RTCisRunning = RTC.isrunning(); 194 | } else { 195 | log_d("[RTC] running :-)"); 196 | RTCisRunning = true; 197 | } 198 | return RTCisRunning; 199 | #else 200 | log_d("[RTC] Hobo mode, no time to waste :)"); 201 | return false; 202 | #endif 203 | } 204 | 205 | 206 | void timeSetup() 207 | { 208 | #if HAS_EXTERNAL_RTC 209 | if(!RTCSetup()) { // RTC failure .... 210 | log_e("RTC Failure, switching to hobo mode"); 211 | } 212 | TimeInit(); 213 | timeHousekeeping(); 214 | #else 215 | uptimeSet(); 216 | #endif 217 | } 218 | -------------------------------------------------------------------------------- /ESP32-BLECollector/GPS.h: -------------------------------------------------------------------------------- 1 | /*\ 2 | * GPS Helpers 3 | \*/ 4 | 5 | 6 | HardwareSerial GPS(1); // uart 1 7 | //#define GPS_RX 33 // io pin number 8 | //#define GPS_TX 32 // io pin number 9 | #define GPS_BAUDRATE 9600 10 | 11 | static unsigned long LastGPSChange = 0; // holds time when last GPS state change or data collection occured 12 | static unsigned long NoGPSSignalSince = 1000; // milliseconds since the last GPS signal or state change 13 | static bool GPSHasFix = false; 14 | unsigned long GPSLastFix = 0; 15 | static bool GPSHasDateTime = false; 16 | static bool GPSDebugToSerial = false; // set this to true to debug, disables GPS decoding and prints to serial instead 17 | static double GPSLat = 0.00; 18 | static double GPSLng = 0.00; 19 | static int GPSFailCounter = 0; 20 | static int GPSFailThreshold = 10; // 2..10 21 | static unsigned long GPSFailCheckDelay = 4000; // check for GPS health every 4s (unit=millis) 22 | 23 | static void GPSInit() 24 | { 25 | LastGPSChange = millis(); 26 | NoGPSSignalSince = 1000; // pretend there was a signal 1 second ago 27 | 28 | GPS.begin(GPS_BAUDRATE, SERIAL_8N1, GPS_RX, GPS_TX); 29 | GPS.flush(); 30 | 31 | } 32 | 33 | #include // https://github.com/mikalhart/TinyGPSPlus 34 | TinyGPSPlus gps; 35 | 36 | static void GPSRead() 37 | { 38 | 39 | if( GPS.available() ) { 40 | do { 41 | if( GPSDebugToSerial ) { 42 | Serial.write( GPS.read() ); 43 | } else { 44 | gps.encode( GPS.read() ); 45 | } 46 | 47 | } while(GPS.available()); 48 | 49 | if( gps.location.isValid() ) { 50 | GPSLat = gps.location.lat(); 51 | GPSLng = gps.location.lng(); 52 | GPSHasFix = true; 53 | GPSLastFix = millis(); 54 | log_d("[GPS] FIX -> LAT: %f LNG: %f", GPSLat, GPSLng ); 55 | } else { 56 | GPSLat = 0.00f; 57 | GPSLng = 0.00f; 58 | } 59 | if(gps.date.isUpdated() && gps.date.isValid() && gps.time.isValid()) { 60 | LastGPSChange = millis(); 61 | GPSHasDateTime = true; // time is valid but date may still be incomplete at this stage 62 | NoGPSSignalSince = 0; 63 | GPSFailCounter = 0; 64 | } else { 65 | // NMEA sent garbage or has no fix yet 66 | NoGPSSignalSince = millis() - LastGPSChange; 67 | } 68 | } else { 69 | // no signal on Serial2 ? 70 | NoGPSSignalSince = millis() - LastGPSChange; 71 | } 72 | 73 | // check for GPS health 74 | // no data in 30 seconds = something's wrong 75 | if (NoGPSSignalSince > GPSFailCheckDelay && gps.charsProcessed() < 10 ) { 76 | if( GPSFailCounter < GPSFailThreshold ) { // don't be spammy 77 | Serial.printf("[GPS] Did not get any data for %ld seconds\n", NoGPSSignalSince/1000); 78 | } else { 79 | // reset fail counter but double the delay 80 | if( GPSFailCheckDelay < 3600000 ) { // max 1h 81 | GPSFailCounter = 0; 82 | GPSFailCheckDelay *=2; 83 | Serial.printf("New GPSFailCheckDelay: %d\n", int(GPSFailCheckDelay) ); 84 | } 85 | if( GPSFailThreshold > 2 ) { // don't go below 2 86 | GPSFailCounter = 0; 87 | GPSFailThreshold--; 88 | Serial.printf("New GPSFailThreshold: %d\n", int(GPSFailThreshold) ); 89 | } 90 | } 91 | LastGPSChange = millis(); // reset timer to reduce spamming in the console 92 | GPSFailCounter++; 93 | } 94 | 95 | } 96 | 97 | 98 | static void getLatLng( void * param ) 99 | { 100 | if( GPSHasFix ) { 101 | Serial.printf("[GPS] Last fix was %d seconds ago -> LAT: %f LNG: %f\n", int( (millis()-GPSLastFix)/1000 ), GPSLat, GPSLng ); 102 | GPSHasFix = false; 103 | } else { 104 | Serial.println("[GPS] had no fix yet"); 105 | } 106 | } 107 | 108 | 109 | static bool setGPSTime() 110 | { 111 | if( GPSFailCounter > 0 ) { 112 | Serial.printf("GPS is not available (%d errors during last %d seconds)\n", int(GPSFailCounter), int(GPSFailCheckDelay/1000) ); 113 | return false; 114 | } 115 | if( !GPSHasDateTime ) { 116 | Serial.println("GPS has no valid DateTime yet"); 117 | return false; 118 | } 119 | 120 | 121 | if(gps.date.isValid() && gps.time.isValid() && gps.date.year() > 2000) { 122 | // TODO: check when this date was collected before trusting it 123 | // e.g. (millis() - LastGPSChange) or NoGPSSignalSince 124 | 125 | // fetch GPS Time 126 | DateTime GPS_UTC_Time = DateTime(gps.date.year(), gps.date.month(), gps.date.day(), gps.time.hour(), gps.time.minute(), gps.time.second()); 127 | // apply timeZone 128 | DateTime GPS_Local_Time = GPS_UTC_Time.unixtime() + (int(timeZone*100)*36) + (summerTime ? 3600 : 0); 129 | #if HAS_EXTERNAL_RTC 130 | RTC.adjust( GPS_Local_Time ); 131 | // TODO: check if RTC.adjust worked 132 | Serial.printf("External RTC adjusted from GPS Time (GMT%s%.2g [%s]): %04d-%02d-%02d %02d:%02d:%02d\n", 133 | timeZone>0 ? "+" : "", 134 | timeZone, 135 | summerTime ? "CEST" : "CET", 136 | GPS_Local_Time.year(), 137 | GPS_Local_Time.month(), 138 | GPS_Local_Time.day(), 139 | GPS_Local_Time.hour(), 140 | GPS_Local_Time.minute(), 141 | GPS_Local_Time.second() 142 | ); 143 | #endif 144 | setTime( GPS_Local_Time.unixtime() ); 145 | timeval epoch = {(time_t)GPS_Local_Time.unixtime(), 0}; 146 | const timeval *tv = &epoch; 147 | settimeofday(tv, NULL); 148 | 149 | struct tm now; 150 | if( !getLocalTime(&now,0) ) { 151 | log_e("Failed to getLocalTime() after setTime() && settimeofday()"); 152 | return false; 153 | } else { 154 | dumpTime( "Internal RTC adjusted from GPS Time", &now ); 155 | logTimeActivity(SOURCE_GPS, GPS_Local_Time.unixtime()); 156 | lastSyncDateTime = GPS_Local_Time; 157 | GPSFailCounter = 0; 158 | return true; 159 | } 160 | 161 | } else { 162 | if (NoGPSSignalSince > GPSFailCheckDelay && gps.charsProcessed() < 10) { 163 | Serial.printf("[GPS] is unavailable, check the config: GPS_RX=%d, GPS_TX=%d, GPS_BAUDRATE=%d\n", GPS_RX, GPS_TX, GPS_BAUDRATE ); 164 | } else { 165 | Serial.println("[GPS] Can't set GPS Time yet, check antenna or wait for a fix?"); 166 | } 167 | return false; 168 | } 169 | } 170 | 171 | // task wrapper 172 | static void setGPSTime( void * param ) 173 | { 174 | setGPSTime(); 175 | } 176 | 177 | 178 | /* 179 | 180 | #include 181 | TinyGPS gps; 182 | 183 | 184 | 185 | DateTime GPSTime; 186 | 187 | static void GPSRead() { 188 | while(GPS.available()) { 189 | gps.encode( GPS.read() ); 190 | } 191 | int year; 192 | byte month, day, hour, minutes, second, hundredths; 193 | unsigned long fix_age; 194 | gps.crack_datetime(&year, &month, &day, &hour, &minutes, &second, &hundredths, &fix_age); 195 | 196 | if( year > 2000 ) { 197 | GPSTime = DateTime( year, month, day, hour, minutes, second ); 198 | LastGPSChange = millis(); 199 | GPSHasDateTime = true; 200 | NoGPSSignalSince = 0; 201 | } else { 202 | NoGPSSignalSince = millis() - LastGPSChange; 203 | } 204 | } 205 | 206 | static void setGPSTime( void * param ) { 207 | if( !GPSHasDateTime ) { 208 | Serial.println("GPS has no valid DateTime, cowardly aborting"); 209 | return; 210 | } 211 | 212 | if( GPSTime.year() > 2000 ) { 213 | //DateTime UTCTime = DateTime(gps.date.year(), gps.date.month(), gps.date.day(), gps.time.hour(), gps.time.minute(), gps.time.second()); 214 | long gap = millis() - LastGPSChange; 215 | DateTime LocalTime = GPSTime.unixtime() + gap + timeZone*3600; 216 | #if HAS_EXTERNAL_RTC 217 | RTC.adjust( LocalTime ); 218 | #endif 219 | setTime( LocalTime.unixtime() ); 220 | Serial.printf("Time adjusted to: %04d-%02d-%02d %02d:%02d:%02d\n", 221 | LocalTime.year(), 222 | LocalTime.month(), 223 | LocalTime.day(), 224 | LocalTime.hour(), 225 | LocalTime.minute(), 226 | LocalTime.second() 227 | ); 228 | logTimeActivity(SOURCE_GPS, LocalTime.unixtime()); 229 | lastSyncDateTime = LocalTime; 230 | } else { 231 | Serial.printf("Can't set GPS Time (no signal since %d seconds)\n", NoGPSSignalSince/1000); 232 | } 233 | } 234 | 235 | */ 236 | -------------------------------------------------------------------------------- /ESP32-BLECollector/RTC.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ESP32 RTC DS1307/BM8563 implementations for the BLE Collector 4 | MIT License 5 | 6 | Copyright (c) 2018 tobozo 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | 26 | ----------------------------------------------------------------------------- 27 | 28 | This driver was baked from different drivers (M5Core2, Adafruit and other forks) 29 | in order to support different time formats as inpout and output, and play well 30 | with other time libraries while supporting a standard syntax for easy 31 | substitution. 32 | 33 | It implements most methods from JeeLabs's library http://news.jeelabs.org/code/ 34 | with added dependencies to PaulStoffregen's Time library https://github.com/PaulStoffregen/Time/ 35 | 36 | */ 37 | 38 | #pragma GCC diagnostic ignored "-Wunused-variable" 39 | 40 | #if defined ARDUINO_M5STACK_Core2 || defined ARDUINO_M5STACK_CORES3 // M5Core2 and M5CoreS3 use BM8563 41 | 42 | //#define BM8563_ADDR 0x51 // M5Core2 RTC I2C address 43 | #define BLE_RTC BLE_RTC_BM8563 // alias for BLECollector 44 | #define M5_RTC M5.Rtc // syntax sugar 45 | 46 | class BLE_RTC_BM8563 47 | { 48 | public: 49 | static bool begin(uint8_t sdaPin=SDA, uint8_t sclPin=SCL); 50 | static void adjust(const tmElements_t& dt); 51 | static void adjust(const time_t& dt); 52 | static void adjust(const DateTime& dt); 53 | uint8_t isrunning(void); 54 | static tmElements_t now(); 55 | static uint32_t unixtime(); 56 | 57 | static void GetTime(tmElements_t &RTC_TimeStruct); 58 | static void GetDate(tmElements_t &RTC_DateStruct); 59 | 60 | static void SetTime(const tmElements_t *RTC_TimeStruct); 61 | static void SetDate(const tmElements_t *RTC_DateStruct); 62 | }; 63 | 64 | // but but but why u no put code in cpp file ??? :-)) 65 | 66 | void BLE_RTC_BM8563::GetTime(tmElements_t &RTC_TimeStruct) 67 | { 68 | RTC_TimeTypeDef tmpTime; 69 | M5_RTC.GetTime( &tmpTime ); 70 | RTC_TimeStruct.Second = tmpTime.Seconds; 71 | RTC_TimeStruct.Minute = tmpTime.Minutes; 72 | RTC_TimeStruct.Hour = tmpTime.Hours; 73 | } 74 | 75 | 76 | void BLE_RTC_BM8563::GetDate(tmElements_t &RTC_DateStruct) 77 | { 78 | RTC_DateTypeDef tmpDate; 79 | M5_RTC.GetDate( &tmpDate ); 80 | RTC_DateStruct.Day = tmpDate.Date; 81 | RTC_DateStruct.Wday = tmpDate.WeekDay; 82 | RTC_DateStruct.Month = tmpDate.Month; 83 | RTC_DateStruct.Year = tmpDate.Year - 1970; 84 | } 85 | 86 | 87 | void BLE_RTC_BM8563::SetTime(const tmElements_t *RTC_TimeStruct) 88 | { 89 | if(RTC_TimeStruct == NULL) 90 | return; 91 | 92 | RTC_TimeTypeDef tmpTime; 93 | tmpTime.Hours = RTC_TimeStruct->Hour; 94 | tmpTime.Minutes = RTC_TimeStruct->Minute; 95 | tmpTime.Seconds = RTC_TimeStruct->Second; 96 | M5_RTC.SetTime( &tmpTime ); 97 | } 98 | 99 | 100 | void BLE_RTC_BM8563::SetDate(const tmElements_t *RTC_DateStruct) 101 | { 102 | if(RTC_DateStruct == NULL) 103 | return; 104 | 105 | RTC_DateTypeDef tmpDate; 106 | tmpDate.Date = RTC_DateStruct->Day; 107 | tmpDate.WeekDay = RTC_DateStruct->Wday; 108 | tmpDate.Month = RTC_DateStruct->Month; 109 | tmpDate.Year = RTC_DateStruct->Year + 1970; 110 | M5_RTC.SetDate( &tmpDate ); 111 | } 112 | 113 | 114 | bool BLE_RTC_BM8563::begin(uint8_t sdaPin, uint8_t sclPin) 115 | { 116 | // rtc begin is now implicit with M5Core2 117 | return true; 118 | } 119 | 120 | 121 | void BLE_RTC_BM8563::adjust(const DateTime& dt) 122 | { 123 | dumpTime("RTC Will adjust from DateTime ", dt.get_tm()); 124 | adjust( dt.get_tm() ); 125 | } 126 | 127 | 128 | void BLE_RTC_BM8563::adjust(const time_t& dt) 129 | { 130 | tmElements_t dateTimeNow; 131 | breakTime(dt, dateTimeNow); 132 | dumpTime("RTC Will adjust from time_t ", dateTimeNow ); 133 | adjust( dateTimeNow ); 134 | } 135 | 136 | 137 | uint32_t BLE_RTC_BM8563::unixtime() 138 | { 139 | return DateTime::tm2unixtime( now() ); 140 | } 141 | 142 | 143 | uint8_t BLE_RTC_BM8563::isrunning(void) 144 | { 145 | return true; // M5_RTC.isrunning() is deprecated 146 | //return M5_RTC.isrunning(); 147 | } 148 | 149 | 150 | void BLE_RTC_BM8563::adjust(const tmElements_t& dt) 151 | { 152 | SetTime( &dt ); 153 | SetDate( &dt ); 154 | } 155 | 156 | 157 | tmElements_t BLE_RTC_BM8563::now() 158 | { 159 | tmElements_t thisTime; 160 | GetTime( thisTime ); 161 | GetDate( thisTime ); 162 | return thisTime; 163 | } 164 | 165 | 166 | 167 | #else // default to DS1307 168 | 169 | 170 | #define DS1307_ADDR 0x68 // I2C address 171 | #define BLE_RTC BLE_RTC_DS1307 // alias for BLECollector 172 | 173 | 174 | class BLE_RTC_DS1307 175 | { 176 | public: 177 | static bool begin(uint8_t sdaPin=SDA, uint8_t sclPin=SCL); 178 | static void adjust(const tmElements_t& dt); 179 | static void adjust(const time_t& dt); 180 | static void adjust(const DateTime& dt); 181 | uint8_t isrunning(void); 182 | static tmElements_t now(); 183 | static uint32_t unixtime(); 184 | }; 185 | 186 | int ZEROINT = 0; 187 | /* 188 | static uint8_t BLE_RTC_bcd2bin (uint8_t val) { return val - 6 * (val >> 4); } 189 | static uint8_t BLE_RTC_bin2bcd (uint8_t val) { return val + 6 * (val / 10); } 190 | */ 191 | 192 | /** Fonction de conversion BCD -> decimal */ 193 | static uint8_t BLE_RTC_bcd2bin (uint8_t bcd) 194 | { 195 | return (bcd / 16 * 10) + (bcd % 16); 196 | } 197 | 198 | /** Fonction de conversion decimal -> BCD */ 199 | static uint8_t BLE_RTC_bin2bcd (uint8_t decimal) 200 | { 201 | return (decimal / 10 * 16) + (decimal % 10); 202 | } 203 | 204 | 205 | bool BLE_RTC_DS1307::begin(uint8_t sdaPin, uint8_t sclPin) 206 | { 207 | Wire.begin(sdaPin, sclPin); 208 | return true; 209 | } 210 | void BLE_RTC_DS1307::adjust(const DateTime& dt) 211 | { 212 | //dumpTime("RTC Will adjust from DateTime ", dt.get_tm()); 213 | adjust( dt.get_tm() ); 214 | } 215 | void BLE_RTC_DS1307::adjust(const time_t& dt) 216 | { 217 | tmElements_t dateTimeNow; 218 | breakTime(dt, dateTimeNow); 219 | //dumpTime("RTC Will adjust from time_t ", dateTimeNow ); 220 | adjust( dateTimeNow ); 221 | } 222 | uint32_t BLE_RTC_DS1307::unixtime() 223 | { 224 | return DateTime::tm2unixtime( now() ); 225 | } 226 | 227 | uint8_t BLE_RTC_DS1307::isrunning(void) 228 | { 229 | Wire.beginTransmission(DS1307_ADDR); 230 | Wire.write(ZEROINT); 231 | Wire.endTransmission(); 232 | Wire.requestFrom(DS1307_ADDR, 1); 233 | uint8_t ss = Wire.read(); 234 | return !(ss>>7); 235 | } 236 | 237 | void BLE_RTC_DS1307::adjust(const tmElements_t& dt) 238 | { 239 | dumpTime("RTC Will adjust from tmElements_t ", dt ); 240 | Wire.beginTransmission(DS1307_ADDR); 241 | Wire.write(ZEROINT); 242 | Wire.write(BLE_RTC_bin2bcd(dt.Second) ); 243 | Wire.write(BLE_RTC_bin2bcd(dt.Minute)); 244 | Wire.write(BLE_RTC_bin2bcd(dt.Hour) ); // mode 24h 245 | Wire.write(BLE_RTC_bin2bcd(dt.Wday)); // day of week ? 246 | Wire.write(BLE_RTC_bin2bcd(dt.Day)); 247 | Wire.write(BLE_RTC_bin2bcd(dt.Month)); 248 | Wire.write(BLE_RTC_bin2bcd(tmYearToY2k(dt.Year))); // 2000 to 1970 offset 249 | Wire.write(ZEROINT); 250 | if ( Wire.endTransmission() != 0 ) log_e( "[RTC] Unexpected end of transmission" ); 251 | } 252 | 253 | 254 | tmElements_t BLE_RTC_DS1307::now() 255 | { 256 | tmElements_t tm; 257 | uint8_t sec; 258 | Wire.beginTransmission( DS1307_ADDR ); 259 | Wire.write( (int)0 ); 260 | if ( Wire.endTransmission() != 0 ) { 261 | log_e( "[RTC] Unexpected end of transmission" ); 262 | return tm; 263 | } 264 | Wire.requestFrom( DS1307_ADDR, 7 ); 265 | sec = Wire.read(); 266 | tm.Second = BLE_RTC_bcd2bin( sec & 0x7f ); 267 | tm.Minute = BLE_RTC_bcd2bin( Wire.read() ); 268 | tm.Hour = BLE_RTC_bcd2bin( Wire.read() & 0x3f ); // mask assumes 24hr clock 269 | tm.Wday = BLE_RTC_bcd2bin( Wire.read() ); 270 | tm.Day = BLE_RTC_bcd2bin( Wire.read() ); 271 | tm.Month = BLE_RTC_bcd2bin( Wire.read() ); 272 | tm.Year = y2kYearToTm( BLE_RTC_bcd2bin( Wire.read() ) ); // 2000 to 1970 offset 273 | if ( sec & 0x80 ) log_e("[RTC] Module is DOWN"); 274 | return tm; 275 | } 276 | 277 | #endif 278 | -------------------------------------------------------------------------------- /ESP32-BLECollector/ScrollPanel.h: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | ESP32 BLE Collector - A BLE scanner with sqlite data persistence on the SD Card 4 | Source: https://github.com/tobozo/ESP32-BLECollector 5 | 6 | MIT License 7 | 8 | Copyright (c) 2018 tobozo 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | ----------------------------------------------------------------------------- 29 | 30 | */ 31 | 32 | #pragma GCC diagnostic ignored "-Wunused-variable" 33 | 34 | #define SPACE " " 35 | static bool isScrolling = false; 36 | static bool isInScroll() 37 | { 38 | return isScrolling; 39 | } 40 | 41 | class ScrollableOutput 42 | { 43 | 44 | enum ScrollType 45 | { 46 | SCROLL_HARDWARE, 47 | SCROLL_SOFTWARE 48 | }; 49 | 50 | public: 51 | uint16_t height;// = scrollpanel_height(); 52 | uint16_t width;// = scrollpanel_width(); 53 | // scroll control variables 54 | uint16_t scrollTopFixedArea = 0; 55 | uint16_t scrollBottomFixedArea = 0; 56 | uint16_t yRef = scrollTopFixedArea; 57 | uint16_t yArea = height - scrollTopFixedArea - scrollBottomFixedArea; 58 | int16_t x1_tmp, y1_tmp; 59 | uint16_t w_tmp, h_tmp; 60 | int scrollPosY = -1; 61 | int scrollPosX = -1; 62 | bool serialEcho = true; 63 | //uint16_t BgColor; 64 | RGBColor BGColorStart; 65 | RGBColor BGColorEnd; 66 | ScrollType scrollType = SCROLL_HARDWARE; 67 | 68 | void init() 69 | { 70 | height = scrollpanel_height(); 71 | width = scrollpanel_width(); 72 | } 73 | 74 | int println() 75 | { 76 | return println(" "); 77 | } 78 | 79 | int println(const char* str) 80 | { 81 | char output[256] = {'\0'}; 82 | sprintf(output, "%s\n", str); 83 | return print(output); 84 | } 85 | 86 | int print(const char* str) 87 | { 88 | if(strcmp(str, " \n")!=0 && serialEcho) { 89 | Serial.print( str ); 90 | } 91 | return scroll(str); 92 | } 93 | 94 | void setupScrollArea(uint16_t TFA, uint16_t BFA, RGBColor colorstart, RGBColor colorend, bool clear = false) 95 | { 96 | BGColorStart = colorstart; 97 | BGColorEnd = colorend; 98 | tft.setCursor(0, TFA); 99 | uint16_t VSA = height-(TFA+BFA); 100 | 101 | scrollTopFixedArea = TFA; 102 | scrollBottomFixedArea = BFA; 103 | 104 | yRef = scrollTopFixedArea; 105 | scrollPosY = scrollTopFixedArea; 106 | yArea = height - scrollTopFixedArea - scrollBottomFixedArea; 107 | 108 | tft_setupHScrollArea(0, 0, 0); // reset ? 109 | tft_setupHScrollArea(TFA, VSA, BFA); 110 | s_x_tmp = 0; 111 | s_y_tmp = scrollTopFixedArea; 112 | s_w_tmp = width; 113 | s_h_tmp = yArea; 114 | 115 | log_d("*** NEW Scroll Setup: Top=%d Bottom=%d YArea=%d", TFA, BFA, yArea); 116 | if (clear) { 117 | tft_fillGradientHRect( 0, TFA, width/2, yArea, BGColorStart, BGColorEnd ); 118 | tft_fillGradientHRect( width/2, TFA, width/2, yArea, BGColorEnd, BGColorStart ); 119 | } 120 | } 121 | 122 | void scrollNextPage() 123 | { 124 | tft_getTextBounds("O", scrollPosX, scrollPosY, &x1_tmp, &y1_tmp, &w_tmp, &h_tmp); 125 | int linesInPage = (yArea-(scrollPosY-scrollTopFixedArea)) / h_tmp; 126 | for(int i=0; i= scrollTopFixedArea && (yStart+_height)<(height-scrollBottomFixedArea) ) { 141 | // no scroll loop point overlap, just render the translated box using the native method 142 | log_v("Rendering native x:%d, y:%d, width:%d, height:%d, radius:%d", x, y, _width, _height, radius); 143 | if( fill ) { 144 | tft.fillRoundRect(x, yStart, _width, _height, radius, bordercolor); 145 | } else { 146 | tft.drawRoundRect(x, yStart, _width, _height, radius, bordercolor); 147 | } 148 | } else { 149 | // box overlaps the scroll limit, split it in two chunks! 150 | int yEnd = translate(y, _height); 151 | int lowerBlockHeight = yEnd - scrollTopFixedArea; 152 | int upperBlockHeight = (scrollTopFixedArea + yArea) - yStart; 153 | int leftVlinePosX = x; 154 | int rightVlinePosX = x+_width-1; 155 | int leftHLinePosX = leftVlinePosX + radius; 156 | int rightHlinePosX = rightVlinePosX - radius; 157 | int hLineWidth = _width - 2 * radius; 158 | log_v("Rendering split x:%d, y:%d, width:%d, height:%d, radius:%d", x, y, _width, _height, radius); 159 | log_v(" yStart:%d, yEnd:%d, upperBlockHeight:%d, lowerBlockHeight:%d", yStart, yEnd, upperBlockHeight, lowerBlockHeight); 160 | 161 | tft.drawFastHLine(leftHLinePosX, yStart, hLineWidth, bordercolor); // upper hline 162 | tft.drawFastHLine(leftHLinePosX, yEnd-1, hLineWidth, bordercolor); // lower hline 163 | if (upperBlockHeight > radius) { 164 | // don't bother rendering the corners if there isn't enough height left 165 | tft.drawFastVLine(leftVlinePosX, yStart + radius, upperBlockHeight - radius, bordercolor); // upper left vline 166 | tft.drawFastVLine(rightVlinePosX, yStart + radius, upperBlockHeight - radius, bordercolor); // upper right vline 167 | } 168 | if (lowerBlockHeight > radius) { 169 | // don't bother rendering the corners if there isn't enough height left 170 | tft.drawFastVLine(leftVlinePosX, yEnd - lowerBlockHeight, lowerBlockHeight - radius, bordercolor); // lower left vline 171 | tft.drawFastVLine(rightVlinePosX, yEnd - lowerBlockHeight, lowerBlockHeight - radius, bordercolor); // lower right vline 172 | } 173 | tft.startWrite(); 174 | tft.drawCircleHelper(leftHLinePosX, yStart + radius, radius, 1, bordercolor); // upper left 175 | tft.drawCircleHelper(rightHlinePosX, yStart + radius, radius, 2, bordercolor); // upper right 176 | tft.drawCircleHelper(rightHlinePosX, yEnd - radius-1, radius, 4, bordercolor); // lower right 177 | tft.drawCircleHelper(leftHLinePosX, yEnd - radius-1, radius, 8, bordercolor); // lower left 178 | tft.endWrite(); 179 | } 180 | } 181 | 182 | private: 183 | 184 | int scroll(const char* str) 185 | { 186 | //tft.drawFastHLine( 0, scrollTopFixedArea, 8, BLE_RED ); 187 | isScrolling = true; 188 | if (scrollPosY == -1) { 189 | scrollPosY = tft.getCursorY(); 190 | } 191 | scrollPosX = tft.getCursorX(); 192 | switch( scrollType ) { 193 | case SCROLL_HARDWARE: 194 | if (scrollPosY >= (height - scrollBottomFixedArea)) { 195 | scrollPosY = (scrollPosY % (height - scrollBottomFixedArea)) + scrollTopFixedArea; 196 | } 197 | tft_getTextBounds(str, scrollPosX, scrollPosY, &x1_tmp, &y1_tmp, &w_tmp, &h_tmp); 198 | break; 199 | case SCROLL_SOFTWARE: 200 | tft_getTextBounds(str, scrollPosX, scrollPosY, &x1_tmp, &y1_tmp, &w_tmp, &h_tmp); 201 | scrollPosY = (scrollTopFixedArea + yArea) - h_tmp; 202 | break; 203 | } 204 | 205 | 206 | tft_fillGradientHRect( 0, scrollPosY, width/2, h_tmp, BGColorStart, BGColorEnd ); 207 | tft_fillGradientHRect( width/2, scrollPosY, width/2, h_tmp, BGColorEnd, BGColorStart ); 208 | tft.setCursor(scrollPosX, scrollPosY); 209 | scroll_slow(h_tmp, 3); // Scroll lines, 3ms per line 210 | //tft.print(str); 211 | if( strcmp(str, " \n")!=0 ) { 212 | tft.drawString( str, tft.getCursorX(), tft.getCursorY()); 213 | } 214 | 215 | scrollPosY = tft.getCursorY() + h_tmp; 216 | tft.setCursor(0, scrollPosY); 217 | isScrolling = false; 218 | return h_tmp; 219 | } 220 | // change this function if your TFT does not handle hardware scrolling 221 | int scroll_slow(int lines, int wait) 222 | { 223 | int yTemp = yRef; 224 | //scrollPosY = -1; 225 | 226 | switch( scrollType ) { 227 | case SCROLL_HARDWARE: 228 | for (int i = 0; i < lines; i++) { 229 | yRef++; 230 | if (yRef == height - scrollBottomFixedArea) yRef = scrollTopFixedArea; 231 | tft_hScrollTo( yRef ); 232 | delay(wait); 233 | } 234 | break; 235 | case SCROLL_SOFTWARE: 236 | tft_scrollTo(-lines); 237 | /* 238 | for (int i = 0; i < lines; i++) { 239 | if (yRef <= scrollTopFixedArea ) yRef = height - scrollBottomFixedArea; 240 | yRef--; 241 | } 242 | */ 243 | yTemp = abs(lines); 244 | break; 245 | } 246 | return yTemp; 247 | } 248 | 249 | }; 250 | 251 | 252 | ScrollableOutput Out; 253 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP32-BLECollector 2 | 3 | [![Join the chat at https://gitter.im/ESP32-BLECollector/ESP32-BLECollector](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ESP32-BLECollector/ESP32-BLECollector) 4 | [![Build Status](https://travis-ci.com/tobozo/ESP32-BLECollector.svg?branch=master)](https://travis-ci.com/github/tobozo/ESP32-BLECollector) 5 | 6 | A BLE Scanner with persistence. 7 | 8 | ![ESP32 BLECollector running on Wrover-Kit](https://user-images.githubusercontent.com/1893754/81865372-1b965680-956e-11ea-9f2d-448c2330f3d3.png) ![ESP32 BLECollector running on M5Stack](https://raw.githubusercontent.com/tobozo/ESP32-BLECollector/unstable/screenshots/BLECollector-M5Stack.jpeg) 9 | 10 | 🎬 [Demo video](https://youtu.be/w5V80PobVWs) 11 | ------------ 12 | 13 | BLECollector is just a passive BLE scanner with a fancy UI. 14 | All BLE data found by the BLE Scanner is collected into a [sqlite3](https://github.com/siara-cc/esp32_arduino_sqlite3_lib) format on the SD Card. 15 | 16 | Public Mac addresses are compared against [OUI list](https://code.wireshark.org/review/gitweb?p=wireshark.git;a=blob_plain;f=manuf), while Vendor names are compared against [BLE Device list](https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers). 17 | 18 | Those two database files are provided in a db format ([mac-oui-light.db](https://github.com/tobozo/ESP32-BLECollector/blob/master/SD/mac-oui-light.db) and [ble-oui.db](https://github.com/tobozo/ESP32-BLECollector/blob/master/SD/ble-oui.db)). 19 | 20 | On first run, a default `blemacs.db` file is created, this is where BLE data will be stored. 21 | When a BLE device is found by the scanner, it is populated with the matching oui/vendor name (if any) and eventually inserted in the `blemasc.db` file. 22 | 23 | ⚠️ This sketch is big! Use the "No OTA (Large Apps)" or "Minimal SPIFFS (Large APPS with OTA)" partition scheme to compile it. 24 | The memory cost of using sqlite and BLE libraries is quite high. 25 | 26 | ⚠️ Builds using ESP32-Wrover can eventually choose the 3.6MB SPIFFS partition scheme, and have the BLECollector working without the SD Card. Experimental support only since SPIFFS tends to get slower and buggy when the partition becomes full. 27 | 28 | 29 | Hardware requirements 30 | --------------------- 31 | - [mandatory] ESP32-Wroom or ESP32-Wrover (Wrover is recommended) 32 | - [mandatory] SD Card (breakout or bundled in Wrover-Kit, M5Stack, Odroid-Go, LoLinD32 Pro) 33 | - [mandatory] Micro SD (FAT32 formatted, **max 4GB**) 34 | - [mandatory] [mac-oui-light.db](https://github.com/tobozo/ESP32-BLECollector/blob/master/SD/mac-oui-light.db) and [ble-oui.db](https://github.com/tobozo/ESP32-BLECollector/blob/master/SD/ble-oui.db) files copied on the Micro SD Card root 35 | - [mandatory] ST7789/ILI9341 320x240 TFT (or bundled in Wrover-Kit, M5Stack, Odroid-Go, LoLinD32 Pro, D-Duino32-XS) 36 | - [optional] (but recommended) I2C RTC Module (see `#define HAS_EXTERNAL_RTC` in Settings.h) 37 | - [optional] Serial GPS Module (see `#define HAS_GPS` in Settings.h) 38 | - [⚠ NEW][optional] [XPad Buttons Shield](https://www.tindie.com/products/deshipu/x-pad-buttons-shield-for-d1-mini-version-60/) from [Radomir Dopieralski](https://github.com/deshipu) 39 | 40 | Software requirements (updated) 41 | --------------------- 42 | - [mandatory] Arduino IDE 43 | - [mandatory] [ESP32-Chimera-Core](https://github.com/tobozo/ESP32-Chimera-Core/) (get it from the Arduino Library Manager) 44 | - [mandatory] [LovyanGFX](https://github.com/Lovyan03/LovyanGFX/) (get it from the Arduino Library Manager) 45 | - [mandatory] [M5Stack-SD-Updater](https://github.com/tobozo/M5Stack-SD-Updater) (get it from the Arduino Library Manager) 46 | - [mandatory] [NimBLE Library](https://github.com/h2zero/NimBLE-Arduino/archive/master.zip) supersedes the BLE (legacy or custom) library versions, install it manually in the Arduino/Libraries folder. 47 | - [mandatory] [PaulStoffregen's Time library](https://github.com/PaulStoffregen/Time) (get it from the Arduino Library Manager) 48 | - [mandatory] [esp32_arduino_sqlite3_lib](https://github.com/siara-cc/esp32_arduino_sqlite3_lib) (get it from the Arduino Library Manager) 49 | - [optional] [TinyGPSPlus](https://github.com/mikalhart/TinyGPSPlus) 50 | 51 | Behaviours (auto-selected except for WiFi): 52 | --------------------------- 53 | - **Hobo**: when no TinyRTC module exists in your build, only uptime will be displayed 54 | - **Rogue**: TinyRTC module adjusted after flashing (build DateTime), shares time over BLE 55 | - **Chronomaniac**: TinyRTC module adjusts itself via GPS, shares time over BLE 56 | - **With WiFi**: Temporary dual BLE/WiFi mode to allow downloading or serving .db files, see `#define WITH_WIFI` in `Settings.h` 57 | 58 | Optional I2C RTC Module requirements 59 | ------------------------------------ 60 | - Wire your TinyRTC to RTC_SDA/RTC_SCL (see `Settings.h` or `Display.h` to override) 61 | - Insert the SD Card 62 | - Set `#define HAS_EXTERNAL_RTC true` in [Settings.h](https://github.com/tobozo/ESP32-BLECollector/blob/master/Settings.h) 63 | - Flash the ESP with partition scheme `Minimal SPIFFS (Large APPS with OTA)` 64 | 65 | Optional Serial GPS Module requirements 66 | --------------------------------------- 67 | - Wire your GPS module to TX1/RX1 (edit `GPS_RX` and `GPS_TX` in GPS.h 68 | - Set `#define HAS_GPS true` in [Settings.h](https://github.com/tobozo/ESP32-BLECollector/blob/master/Settings.h) 69 | - Flash the ESP with partition scheme `Minimal SPIFFS (Large APPS with OTA)` 70 | - Wait for the GPS to find a fix 71 | - issue the command `gpstime` in the serial console 72 | 73 | Optional XPad Buttons Shield requirements 74 | ----------------------------------------- 75 | - Wire your XPad Buttons Shield to XPAD_SDA/XPAD_SCL (see `HID_XPad.h` to override) 76 | - Enable the module in `Display.h` : `#define hasXPaxShield() (bool) true` 77 | - Controls are: 78 | - Down / Up : brightness 79 | - Right / Left : unassigned (yet) 80 | - A : start/stop scan 81 | - B / C : toggle mac filter 82 | - D : unassigned (yet) 83 | 84 | Time Sharing 85 | ------------ 86 | - Once the time is set using RTC, GPS or NTP, the BLECollector may start the TimeSharing service and advertise a DateTime characteristic for other BLECollectors to sync with. 87 | - Builds with no RTC/GPS will try to identify this service during their scan duty cycle and subscribe for notifications. 88 | 89 | File Downloading (still experimental) 90 | ------------ 91 | 92 | Sending the `DownloadDB` command will: 93 | 94 | - Stop BLE 95 | - Start WiFi 96 | - Synchronize time to a nearby NTP server 97 | - Download the latest oui/vendors database from github 98 | 99 | 100 | Serial command interface 101 | ------------ 102 | 103 | Available Commands: 104 | 105 | 01) help : Print this list 106 | 02) halp : Same as help except it doesn't print anything 107 | 03) start : Start/resume scan 108 | 04) stop : Stop scan 109 | 05) toggleFilter : Toggle vendor filter on the TFT (persistent) 110 | 06) toggleEcho : Toggle BLECards in the Serial Console (persistent) 111 | 07) setTimeZone : Set the timezone for next NTP Sync (persistent) 112 | 08) setSummerTime : Toggle CEST / CET for next NTP Sync (persistent) 113 | 09) dump : Dump returning BLE devices to the display and updates DB 114 | 10) setBrightness : Set brightness to [value] (0-255) (persistent) 115 | 11) ls : Show [dir] Content on the SD 116 | 12) rm : Delete [file] from the SD 117 | 13) restart : Restart BLECollector ('restart now' to skip replication) 118 | 14) screenshot : Make a screenshot and save it on the SD 119 | 15) screenshow : Show screenshot 120 | 16) toggle : toggle a bool value 121 | 17) resetDB : Hard Reset DB + forced restart 122 | 18) pruneDB : Soft Reset DB without restarting (hopefully) 123 | 19) bleclock : Broadcast time to another BLE Device (implicit) 124 | 20) bletime : Get time from another BLE Device (explicit) 125 | 21) gpstime : Sync time from GPS 126 | 22) latlng : Print the GPS lat/lng 127 | 23) stopBLE : Stop BLE (use 'restart' command to re-enable) 128 | 24) startWiFi : Start WiFi (will stop BLE) 129 | 25) setPoolZone : Set NTP Pool Zone for next NTP Sync (persistent) 130 | 26) NTPSync : Update time from NTP (will start WiFi) 131 | 27) DownloadDB : Download or update db files (will start WiFi and update NTP first) 132 | 28) setWiFiSSID : Set WiFi SSID 133 | 29) setWiFiPASS : Set WiFi Password 134 | 135 | 136 | Contributions are welcome :-) 137 | 138 | 139 | Known issues / Roadmap 140 | ---------------------- 141 | 142 | Implementing both [LovyanGFX](https://github.com/lovyan03/LovyanGFX) and [Nimble-Arduino](https://github.com/h2zero/NimBLE-Arduino) was such a huge optimization that none of the previous blockers exist any more! 143 | 144 | Some ideas I'll try to implement in the upcoming changes: 145 | 146 | - Add GPS Coords to entries for better pruning [as suggested by /u/playaspect](https://www.reddit.com/r/esp8266/comments/9s594c/esp32blecollector_ble_scanner_data_persistence_on/e8nipr6/?context=3) 147 | - Better Analysis of ServiceData (see @reelyactive's [advlib](https://github.com/reelyactive/advlib)) 148 | - Extended logging (SDCard-less meshed builds) 149 | 150 | 151 | Other ESP32 security related tools: 152 | ----------------------------------- 153 | 154 | - https://github.com/cyberman54/ESP32-Paxcounter 155 | - https://github.com/G4lile0/ESP32-WiFi-Hash-Monster 156 | - https://github.com/justcallmekoko/ESP32Marauder 157 | 158 | 159 | Credits/requirements: 160 | --------------------- 161 | 162 | - @Lovyan03 for integrating his [LovyanGFX](https://github.com/lovyan03/LovyanGFX) into the [ESP32-Chimera-Core](https://github.com/tobozo/ESP32-Chimera-Core/tree/lgfx_test) thus saving an enormous amount of sram and flash space 163 | - @h2zero for sharing [NimBLE Library](https://github.com/h2zero/NimBLE-Arduino/) and brillantly proving that BLE can work with WiFi on Arduino without eating all sram/flash space 164 | - https://github.com/siara-cc/esp32_arduino_sqlite3_lib 165 | - huge thanks to https://github.com/chegewara for maintaining the initial [BLE library](https://github.com/tobozo/ESP32-BLECollector/releases/download/1.2/BLE.zip) that made this project possible 166 | 167 | -------------------------------------------------------------------------------- /log: -------------------------------------------------------------------------------- 1 | Processing m5stack-fire (board: m5stack-fire; platform: espressif32; framework: arduino) 2 | -------------------------------------------------------------------------------- 3 | Verbose mode can be enabled via `-v, --verbose` option 4 | CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/m5stack-fire.html 5 | PLATFORM: Espressif 32 (3.3.2) > M5Stack FIRE 6 | HARDWARE: ESP32 240MHz, 4.31MB RAM, 16MB Flash 7 | DEBUG: Current (esp-prog) External (esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa) 8 | PACKAGES: 9 | - framework-arduinoespressif32 3.10006.210326 (1.0.6) 10 | - tool-esptoolpy 1.30100.210531 (3.1.0) 11 | - tool-mkspiffs 2.230.0 (2.30) 12 | - toolchain-xtensa32 2.50200.97 (5.2.0) 13 | Converting ESP32-BLECollector.ino 14 | LDF: Library Dependency Finder -> http://bit.ly/configure-pio-ldf 15 | LDF Modes: Finder ~ chain, Compatibility ~ soft 16 | Library Manager: Installing SD 17 | Library Manager: Warning! More than one package has been found by SD requirements: 18 | - adafruit/SD @ 0.0.0-alpha+sha.041f788250 19 | - arduino-libraries/SD @ 1.2.4 20 | - rei-vilo/SD @ 0.0.0-alpha+sha.a8a1454af9 21 | - mbed-jackson-lv/SD @ 0.0.0+sha.405b46e831df 22 | - mbed-1529561000/SD @ 0.0.0+sha.db4599aff06a 23 | Library Manager: Please specify detailed REQUIREMENTS using package owner and version (showed above) to avoid name conflicts 24 | Unpacking 25 | Library Manager: SD @ 0.0.0-alpha+sha.041f788250 has been installed! 26 | Library Manager: Installing LovyanGFX 27 | Unpacking 28 | Library Manager: LovyanGFX @ 0.4.5 has been installed! 29 | Library Manager: Installing ESP32-Chimera-Core 30 | Unpacking 31 | Library Manager: ESP32-Chimera-Core @ 1.2.5 has been installed! 32 | Library Manager: Installing dependencies... 33 | Library Manager: Installing Time 34 | Unpacking 35 | Library Manager: Time @ 1.6.1 has been installed! 36 | Library Manager: Installing M5Stack-SD-Updater 37 | Unpacking 38 | Library Manager: M5Stack-SD-Updater @ 1.1.4 has been installed! 39 | Library Manager: Installing Sqlite3Esp32 40 | Unpacking 41 | Library Manager: Sqlite3Esp32 @ 2.3.0 has been installed! 42 | Library Manager: Installing TinyGPSPlus 43 | Library Manager: Warning! More than one package has been found by TinyGPSPlus requirements: 44 | - mikalhart/TinyGPSPlus @ 1.0.2 45 | - mbed-aoba/TinyGPSPlus @ 0.0.0+sha.6d3813637f20 46 | - mbed-okiaviation/TinyGPSPlus @ 0.0.0+sha.2576f9f1dc71 47 | Library Manager: Please specify detailed REQUIREMENTS using package owner and version (showed above) to avoid name conflicts 48 | Unpacking 49 | Library Manager: TinyGPSPlus @ 1.0.2 has been installed! 50 | Library Manager: Installing NimBLE-Arduino 51 | Unpacking 52 | Library Manager: NimBLE-Arduino @ 1.3.1 has been installed! 53 | Library Manager: Installing FastLED @ 3.4.0 54 | Library Manager: Warning! More than one package has been found by FastLED @ 3.4.0 requirements: 55 | - fastled/FastLED @ 3.4.0 56 | - etidbury/FastLED @ 3.3.3 57 | Library Manager: Please specify detailed REQUIREMENTS using package owner and version (showed above) to avoid name conflicts 58 | Unpacking 59 | Library Manager: FastLED @ 3.4.0 has been installed! 60 | Found 37 compatible libraries 61 | Scanning dependencies... 62 | Dependency Graph 63 | |-- 0.0.0-alpha+sha.041f788250 64 | | |-- 1.0 65 | |-- 0.4.5 66 | | |-- 1.0 67 | | |-- 1.0.1 68 | |-- 1.2.5 69 | | |-- 0.4.5 70 | | | |-- 1.0 71 | | | |-- 1.0.1 72 | | |-- 1.0 73 | | |-- 1.0.1 74 | | |-- 1.0 75 | | |-- 1.0 76 | | |-- 0.0.0-alpha+sha.041f788250 77 | | | |-- 1.0 78 | | |-- 1.0 79 | | | |-- 1.0 80 | |--