├── .github └── workflows │ └── main.yml ├── .gitignore ├── .gitmodules ├── .vscode └── extensions.json ├── CMakeLists.txt ├── LICENSE ├── README.md ├── components └── minmea │ └── CMakeLists.txt ├── esp-gps-ntp.code-workspace ├── images └── esp-gps-ntp.jpg ├── kicad ├── display-adapter │ ├── display-adapter-cache.lib │ ├── display-adapter.kicad_pcb │ ├── display-adapter.pro │ ├── display-adapter.sch │ └── fp-info-cache └── esp-gps-ntp │ ├── esp-gps-ntp-cache.lib │ ├── esp-gps-ntp.kicad_pcb │ ├── esp-gps-ntp.pro │ ├── esp-gps-ntp.sch │ └── fp-info-cache ├── main ├── CMakeLists.txt ├── Config.cpp ├── Config.h ├── DS3231.cpp ├── DS3231.h ├── Display.cpp ├── Display.h ├── FieldText.cpp ├── FieldText.h ├── GPS.cpp ├── GPS.h ├── Kconfig.projbuild ├── MicroSecondTimer.cpp ├── MicroSecondTimer.h ├── NTP.cpp ├── NTP.h ├── Network.cpp ├── Network.h ├── PPS.cpp ├── PPS.h ├── PageAbout.cpp ├── PageAbout.h ├── PageConfig.cpp ├── PageConfig.h ├── PageDelta.cpp ├── PageDelta.h ├── PageGPS.cpp ├── PageGPS.h ├── PageNTP.cpp ├── PageNTP.h ├── PagePPS.cpp ├── PagePPS.h ├── PageSats.cpp ├── PageSats.h ├── PageSync.cpp ├── PageSync.h ├── SyncManager.cpp ├── SyncManager.h ├── WithDisplayLock.cpp ├── WithDisplayLock.h ├── highint5.S └── main.cpp ├── partitions.csv ├── platformio.ini ├── sdkconfig.defaults └── test └── README /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a basic workflow to help you get started with Actions 2 | 3 | name: CI 4 | 5 | # Controls when the action will run. 6 | on: 7 | # Triggers the workflow on push or pull request events but only for the master branch 8 | push: 9 | branches: [ master ] 10 | pull_request: 11 | branches: [ master ] 12 | 13 | # Allows you to run this workflow manually from the Actions tab 14 | workflow_dispatch: 15 | 16 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 17 | jobs: 18 | # This workflow contains a single job called "build" 19 | build: 20 | # The type of runner that the job will run on 21 | runs-on: ubuntu-latest 22 | 23 | # Steps represent a sequence of tasks that will be executed as part of the job 24 | steps: 25 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 26 | - uses: actions/checkout@v2 27 | with: 28 | submodules: recursive 29 | 30 | - name: Cache pip 31 | uses: actions/cache@v2 32 | with: 33 | path: ~/.cache/pip 34 | key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} 35 | restore-keys: | 36 | ${{ runner.os }}-pip- 37 | 38 | - name: Cache PlatformIO 39 | uses: actions/cache@v2 40 | with: 41 | path: ~/.platformio 42 | key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} 43 | 44 | - name: Set up Python 45 | uses: actions/setup-python@v2 46 | 47 | - name: Install PlatformIO 48 | run: | 49 | python -m pip install --upgrade pip 50 | pip install --upgrade platformio 51 | 52 | - name: Run PlatformIO 53 | run: pio run -e default 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /local.ini 2 | /.pio 3 | /.vscode/.browse.c_cpp.db* 4 | /.vscode/c_cpp_properties.json 5 | /.vscode/launch.json 6 | /.vscode/ipch 7 | /sdkconfig 8 | /sdkconfig.old 9 | /sdkconfig.defaults.old 10 | .DS_Store 11 | *-bak 12 | *.net 13 | *.gbr 14 | *.zip 15 | *.drl 16 | *.step 17 | 18 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "components/lvgl_cpp"] 2 | path = components/lvgl_cpp 3 | url = https://github.com/liebman/lvgl_cpp.git 4 | [submodule "components/minmea/minmea"] 5 | path = components/minmea/minmea 6 | url = https://github.com/kosma/minmea.git 7 | [submodule "components/lvgl"] 8 | path = components/lvgl 9 | url = https://github.com/lvgl/lvgl.git 10 | [submodule "components/lvgl_esp32_drivers"] 11 | path = components/lvgl_esp32_drivers 12 | url = https://github.com/lvgl/lvgl_esp32_drivers.git 13 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16.0) 2 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 3 | list(APPEND EXTRA_COMPONENT_DIRS components/lvgl_esp32_drivers components/lvgl_esp32_drivers/lvgl_touch components/lvgl_esp32_drivers/lvgl_tft) 4 | add_compile_definitions(LV_LVGL_H_INCLUDE_SIMPLE=1) 5 | project(esp-gps-ntp) 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Christopher B. Liebman 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # This is an ESP32 NTP Server with DS3231 synched to GPS PPS signal 2 | 3 | **NOTE: This is functional but *very much* a work in progress at the moment.** 4 | 5 | This is an [esp-idf](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/index.html) app using [platformio](https://platformio.org/). 6 | 7 | ## Some details 8 | 9 | Synching the DS3231 to the GPS is done with a small high level (level 5) interrupt handler in assembly. This generates a timestamp and offset in microseconds tracking the active edges of the GPS PPS and RTC SQW signals. This data is then fed in to a PID algorithm that will generate an offset value used to speed up and slow down the DS3231 RTC. This keeps the DS3231 synced with the GPS to within a couple of microseconds. As a side benifit it also tunes the DS3231's ocilator to reduce drift when GPS is unavailable. 10 | 11 | - [main](main) Contains the code 12 | - [kicad/esp-gps-ntp](kicad/esp-gps-ntp) contains the schematic and board designs in KiCad. 13 | - [kicad/display-adapter](kicad/display-adapter) contains the schematic and board design for a small adapter to config a single inline header connector to an IDC connector (for ribbon cable connection of display) 14 | 15 | The display I'm using is [here](https://www.amazon.com/gp/product/B073R7BH1B) 16 | 17 | ## Schematic 18 | 19 | ![Schematic](images/esp-gps-ntp.jpg) 20 | -------------------------------------------------------------------------------- /components/minmea/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | idf_component_register(SRCS minmea/minmea.c 3 | INCLUDE_DIRS minmea) 4 | 5 | target_compile_definitions(${COMPONENT_TARGET} PRIVATE 6 | timegm=mktime) 7 | -------------------------------------------------------------------------------- /esp-gps-ntp.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "C_Cpp.default.intelliSenseMode": "gcc-x64", 9 | "files.associations": { 10 | "*.ipp": "cpp" 11 | }, 12 | "C_Cpp.default.cppStandard": "c++17" 13 | } 14 | } -------------------------------------------------------------------------------- /images/esp-gps-ntp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liebman/esp32-gps-ntp/42643aa624090f5227f2559f8a956769cfdf1a2e/images/esp-gps-ntp.jpg -------------------------------------------------------------------------------- /kicad/display-adapter/display-adapter-cache.lib: -------------------------------------------------------------------------------- 1 | EESchema-LIBRARY Version 2.4 2 | #encoding utf-8 3 | # 4 | # Connector_Conn_01x14_Female 5 | # 6 | DEF Connector_Conn_01x14_Female J 0 40 Y N 1 F N 7 | F0 "J" 0 700 50 H V C CNN 8 | F1 "Connector_Conn_01x14_Female" 0 -800 50 H V C CNN 9 | F2 "" 0 0 50 H I C CNN 10 | F3 "" 0 0 50 H I C CNN 11 | $FPLIST 12 | Connector*:*_1x??_* 13 | $ENDFPLIST 14 | DRAW 15 | A 0 -700 20 901 -901 1 1 6 N 0 -680 0 -720 16 | A 0 -600 20 901 -901 1 1 6 N 0 -580 0 -620 17 | A 0 -500 20 901 -901 1 1 6 N 0 -480 0 -520 18 | A 0 -400 20 901 -901 1 1 6 N 0 -380 0 -420 19 | A 0 -300 20 901 -901 1 1 6 N 0 -280 0 -320 20 | A 0 -200 20 901 -901 1 1 6 N 0 -180 0 -220 21 | A 0 -100 20 901 -901 1 1 6 N 0 -80 0 -120 22 | A 0 0 20 901 -901 1 1 6 N 0 20 0 -20 23 | A 0 100 20 901 -901 1 1 6 N 0 120 0 80 24 | A 0 200 20 901 -901 1 1 6 N 0 220 0 180 25 | A 0 300 20 901 -901 1 1 6 N 0 320 0 280 26 | A 0 400 20 901 -901 1 1 6 N 0 420 0 380 27 | A 0 500 20 901 -901 1 1 6 N 0 520 0 480 28 | A 0 600 20 901 -901 1 1 6 N 0 620 0 580 29 | P 2 1 1 6 -50 -700 -20 -700 N 30 | P 2 1 1 6 -50 -600 -20 -600 N 31 | P 2 1 1 6 -50 -500 -20 -500 N 32 | P 2 1 1 6 -50 -400 -20 -400 N 33 | P 2 1 1 6 -50 -300 -20 -300 N 34 | P 2 1 1 6 -50 -200 -20 -200 N 35 | P 2 1 1 6 -50 -100 -20 -100 N 36 | P 2 1 1 6 -50 0 -20 0 N 37 | P 2 1 1 6 -50 100 -20 100 N 38 | P 2 1 1 6 -50 200 -20 200 N 39 | P 2 1 1 6 -50 300 -20 300 N 40 | P 2 1 1 6 -50 400 -20 400 N 41 | P 2 1 1 6 -50 500 -20 500 N 42 | P 2 1 1 6 -50 600 -20 600 N 43 | X Pin_1 1 -200 600 150 R 50 50 1 1 P 44 | X Pin_10 10 -200 -300 150 R 50 50 1 1 P 45 | X Pin_11 11 -200 -400 150 R 50 50 1 1 P 46 | X Pin_12 12 -200 -500 150 R 50 50 1 1 P 47 | X Pin_13 13 -200 -600 150 R 50 50 1 1 P 48 | X Pin_14 14 -200 -700 150 R 50 50 1 1 P 49 | X Pin_2 2 -200 500 150 R 50 50 1 1 P 50 | X Pin_3 3 -200 400 150 R 50 50 1 1 P 51 | X Pin_4 4 -200 300 150 R 50 50 1 1 P 52 | X Pin_5 5 -200 200 150 R 50 50 1 1 P 53 | X Pin_6 6 -200 100 150 R 50 50 1 1 P 54 | X Pin_7 7 -200 0 150 R 50 50 1 1 P 55 | X Pin_8 8 -200 -100 150 R 50 50 1 1 P 56 | X Pin_9 9 -200 -200 150 R 50 50 1 1 P 57 | ENDDRAW 58 | ENDDEF 59 | # 60 | # Connector_Generic_Conn_02x07_Odd_Even 61 | # 62 | DEF Connector_Generic_Conn_02x07_Odd_Even J 0 40 Y N 1 F N 63 | F0 "J" 50 400 50 H V C CNN 64 | F1 "Connector_Generic_Conn_02x07_Odd_Even" 50 -400 50 H V C CNN 65 | F2 "" 0 0 50 H I C CNN 66 | F3 "" 0 0 50 H I C CNN 67 | $FPLIST 68 | Connector*:*_2x??_* 69 | $ENDFPLIST 70 | DRAW 71 | S -50 -295 0 -305 1 1 6 N 72 | S -50 -195 0 -205 1 1 6 N 73 | S -50 -95 0 -105 1 1 6 N 74 | S -50 5 0 -5 1 1 6 N 75 | S -50 105 0 95 1 1 6 N 76 | S -50 205 0 195 1 1 6 N 77 | S -50 305 0 295 1 1 6 N 78 | S -50 350 150 -350 1 1 10 f 79 | S 150 -295 100 -305 1 1 6 N 80 | S 150 -195 100 -205 1 1 6 N 81 | S 150 -95 100 -105 1 1 6 N 82 | S 150 5 100 -5 1 1 6 N 83 | S 150 105 100 95 1 1 6 N 84 | S 150 205 100 195 1 1 6 N 85 | S 150 305 100 295 1 1 6 N 86 | X Pin_1 1 -200 300 150 R 50 50 1 1 P 87 | X Pin_10 10 300 -100 150 L 50 50 1 1 P 88 | X Pin_11 11 -200 -200 150 R 50 50 1 1 P 89 | X Pin_12 12 300 -200 150 L 50 50 1 1 P 90 | X Pin_13 13 -200 -300 150 R 50 50 1 1 P 91 | X Pin_14 14 300 -300 150 L 50 50 1 1 P 92 | X Pin_2 2 300 300 150 L 50 50 1 1 P 93 | X Pin_3 3 -200 200 150 R 50 50 1 1 P 94 | X Pin_4 4 300 200 150 L 50 50 1 1 P 95 | X Pin_5 5 -200 100 150 R 50 50 1 1 P 96 | X Pin_6 6 300 100 150 L 50 50 1 1 P 97 | X Pin_7 7 -200 0 150 R 50 50 1 1 P 98 | X Pin_8 8 300 0 150 L 50 50 1 1 P 99 | X Pin_9 9 -200 -100 150 R 50 50 1 1 P 100 | ENDDRAW 101 | ENDDEF 102 | # 103 | # power_+5V 104 | # 105 | DEF power_+5V #PWR 0 0 Y Y 1 F P 106 | F0 "#PWR" 0 -150 50 H I C CNN 107 | F1 "power_+5V" 0 140 50 H V C CNN 108 | F2 "" 0 0 50 H I C CNN 109 | F3 "" 0 0 50 H I C CNN 110 | DRAW 111 | P 2 0 1 0 -30 50 0 100 N 112 | P 2 0 1 0 0 0 0 100 N 113 | P 2 0 1 0 0 100 30 50 N 114 | X +5V 1 0 0 0 U 50 50 1 1 W N 115 | ENDDRAW 116 | ENDDEF 117 | # 118 | # power_GND 119 | # 120 | DEF power_GND #PWR 0 0 Y Y 1 F P 121 | F0 "#PWR" 0 -250 50 H I C CNN 122 | F1 "power_GND" 0 -150 50 H V C CNN 123 | F2 "" 0 0 50 H I C CNN 124 | F3 "" 0 0 50 H I C CNN 125 | DRAW 126 | P 6 0 1 0 0 0 0 -50 50 -50 0 -100 -50 -50 0 -50 N 127 | X GND 1 0 0 0 D 50 50 1 1 W N 128 | ENDDRAW 129 | ENDDEF 130 | # 131 | #End Library 132 | -------------------------------------------------------------------------------- /kicad/display-adapter/display-adapter.pro: -------------------------------------------------------------------------------- 1 | update=Saturday, December 05, 2020 at 02:14:10 PM 2 | version=1 3 | last_client=kicad 4 | [general] 5 | version=1 6 | RootSch= 7 | BoardNm= 8 | [cvpcb] 9 | version=1 10 | NetIExt=net 11 | [eeschema] 12 | version=1 13 | LibDir= 14 | [eeschema/libraries] 15 | [schematic_editor] 16 | version=1 17 | PageLayoutDescrFile= 18 | PlotDirectoryName= 19 | SubpartIdSeparator=0 20 | SubpartFirstId=65 21 | NetFmtName=Pcbnew 22 | SpiceAjustPassiveValues=0 23 | LabSize=50 24 | ERC_TestSimilarLabels=1 25 | [pcbnew] 26 | version=1 27 | PageLayoutDescrFile= 28 | LastNetListRead=display-adapter.net 29 | CopperLayerCount=2 30 | BoardThickness=1.6 31 | AllowMicroVias=0 32 | AllowBlindVias=0 33 | RequireCourtyardDefinitions=0 34 | ProhibitOverlappingCourtyards=1 35 | MinTrackWidth=0.2 36 | MinViaDiameter=0.4 37 | MinViaDrill=0.3 38 | MinMicroViaDiameter=0.2 39 | MinMicroViaDrill=0.09999999999999999 40 | MinHoleToHole=0.25 41 | TrackWidth1=0.25 42 | ViaDiameter1=0.8 43 | ViaDrill1=0.4 44 | dPairWidth1=0.2 45 | dPairGap1=0.25 46 | dPairViaGap1=0.25 47 | SilkLineWidth=0.12 48 | SilkTextSizeV=1 49 | SilkTextSizeH=1 50 | SilkTextSizeThickness=0.15 51 | SilkTextItalic=0 52 | SilkTextUpright=1 53 | CopperLineWidth=0.2 54 | CopperTextSizeV=1.5 55 | CopperTextSizeH=1.5 56 | CopperTextThickness=0.3 57 | CopperTextItalic=0 58 | CopperTextUpright=1 59 | EdgeCutLineWidth=0.05 60 | CourtyardLineWidth=0.05 61 | OthersLineWidth=0.15 62 | OthersTextSizeV=1 63 | OthersTextSizeH=1 64 | OthersTextSizeThickness=0.15 65 | OthersTextItalic=0 66 | OthersTextUpright=1 67 | SolderMaskClearance=0 68 | SolderMaskMinWidth=0 69 | SolderPasteClearance=0 70 | SolderPasteRatio=-0 71 | [pcbnew/Layer.F.Cu] 72 | Name=F.Cu 73 | Type=0 74 | Enabled=1 75 | [pcbnew/Layer.In1.Cu] 76 | Name=In1.Cu 77 | Type=0 78 | Enabled=0 79 | [pcbnew/Layer.In2.Cu] 80 | Name=In2.Cu 81 | Type=0 82 | Enabled=0 83 | [pcbnew/Layer.In3.Cu] 84 | Name=In3.Cu 85 | Type=0 86 | Enabled=0 87 | [pcbnew/Layer.In4.Cu] 88 | Name=In4.Cu 89 | Type=0 90 | Enabled=0 91 | [pcbnew/Layer.In5.Cu] 92 | Name=In5.Cu 93 | Type=0 94 | Enabled=0 95 | [pcbnew/Layer.In6.Cu] 96 | Name=In6.Cu 97 | Type=0 98 | Enabled=0 99 | [pcbnew/Layer.In7.Cu] 100 | Name=In7.Cu 101 | Type=0 102 | Enabled=0 103 | [pcbnew/Layer.In8.Cu] 104 | Name=In8.Cu 105 | Type=0 106 | Enabled=0 107 | [pcbnew/Layer.In9.Cu] 108 | Name=In9.Cu 109 | Type=0 110 | Enabled=0 111 | [pcbnew/Layer.In10.Cu] 112 | Name=In10.Cu 113 | Type=0 114 | Enabled=0 115 | [pcbnew/Layer.In11.Cu] 116 | Name=In11.Cu 117 | Type=0 118 | Enabled=0 119 | [pcbnew/Layer.In12.Cu] 120 | Name=In12.Cu 121 | Type=0 122 | Enabled=0 123 | [pcbnew/Layer.In13.Cu] 124 | Name=In13.Cu 125 | Type=0 126 | Enabled=0 127 | [pcbnew/Layer.In14.Cu] 128 | Name=In14.Cu 129 | Type=0 130 | Enabled=0 131 | [pcbnew/Layer.In15.Cu] 132 | Name=In15.Cu 133 | Type=0 134 | Enabled=0 135 | [pcbnew/Layer.In16.Cu] 136 | Name=In16.Cu 137 | Type=0 138 | Enabled=0 139 | [pcbnew/Layer.In17.Cu] 140 | Name=In17.Cu 141 | Type=0 142 | Enabled=0 143 | [pcbnew/Layer.In18.Cu] 144 | Name=In18.Cu 145 | Type=0 146 | Enabled=0 147 | [pcbnew/Layer.In19.Cu] 148 | Name=In19.Cu 149 | Type=0 150 | Enabled=0 151 | [pcbnew/Layer.In20.Cu] 152 | Name=In20.Cu 153 | Type=0 154 | Enabled=0 155 | [pcbnew/Layer.In21.Cu] 156 | Name=In21.Cu 157 | Type=0 158 | Enabled=0 159 | [pcbnew/Layer.In22.Cu] 160 | Name=In22.Cu 161 | Type=0 162 | Enabled=0 163 | [pcbnew/Layer.In23.Cu] 164 | Name=In23.Cu 165 | Type=0 166 | Enabled=0 167 | [pcbnew/Layer.In24.Cu] 168 | Name=In24.Cu 169 | Type=0 170 | Enabled=0 171 | [pcbnew/Layer.In25.Cu] 172 | Name=In25.Cu 173 | Type=0 174 | Enabled=0 175 | [pcbnew/Layer.In26.Cu] 176 | Name=In26.Cu 177 | Type=0 178 | Enabled=0 179 | [pcbnew/Layer.In27.Cu] 180 | Name=In27.Cu 181 | Type=0 182 | Enabled=0 183 | [pcbnew/Layer.In28.Cu] 184 | Name=In28.Cu 185 | Type=0 186 | Enabled=0 187 | [pcbnew/Layer.In29.Cu] 188 | Name=In29.Cu 189 | Type=0 190 | Enabled=0 191 | [pcbnew/Layer.In30.Cu] 192 | Name=In30.Cu 193 | Type=0 194 | Enabled=0 195 | [pcbnew/Layer.B.Cu] 196 | Name=B.Cu 197 | Type=0 198 | Enabled=1 199 | [pcbnew/Layer.B.Adhes] 200 | Enabled=1 201 | [pcbnew/Layer.F.Adhes] 202 | Enabled=1 203 | [pcbnew/Layer.B.Paste] 204 | Enabled=1 205 | [pcbnew/Layer.F.Paste] 206 | Enabled=1 207 | [pcbnew/Layer.B.SilkS] 208 | Enabled=1 209 | [pcbnew/Layer.F.SilkS] 210 | Enabled=1 211 | [pcbnew/Layer.B.Mask] 212 | Enabled=1 213 | [pcbnew/Layer.F.Mask] 214 | Enabled=1 215 | [pcbnew/Layer.Dwgs.User] 216 | Enabled=1 217 | [pcbnew/Layer.Cmts.User] 218 | Enabled=1 219 | [pcbnew/Layer.Eco1.User] 220 | Enabled=1 221 | [pcbnew/Layer.Eco2.User] 222 | Enabled=1 223 | [pcbnew/Layer.Edge.Cuts] 224 | Enabled=1 225 | [pcbnew/Layer.Margin] 226 | Enabled=1 227 | [pcbnew/Layer.B.CrtYd] 228 | Enabled=1 229 | [pcbnew/Layer.F.CrtYd] 230 | Enabled=1 231 | [pcbnew/Layer.B.Fab] 232 | Enabled=1 233 | [pcbnew/Layer.F.Fab] 234 | Enabled=1 235 | [pcbnew/Layer.Rescue] 236 | Enabled=0 237 | [pcbnew/Netclasses] 238 | [pcbnew/Netclasses/Default] 239 | Name=Default 240 | Clearance=0.2 241 | TrackWidth=0.25 242 | ViaDiameter=0.8 243 | ViaDrill=0.4 244 | uViaDiameter=0.3 245 | uViaDrill=0.1 246 | dPairWidth=0.2 247 | dPairGap=0.25 248 | dPairViaGap=0.25 249 | [pcbnew/Netclasses/1] 250 | Name=Power 251 | Clearance=0.2 252 | TrackWidth=0.5 253 | ViaDiameter=0.8 254 | ViaDrill=0.4 255 | uViaDiameter=0.3 256 | uViaDrill=0.1 257 | dPairWidth=0.2 258 | dPairGap=0.25 259 | dPairViaGap=0.25 260 | -------------------------------------------------------------------------------- /kicad/display-adapter/display-adapter.sch: -------------------------------------------------------------------------------- 1 | EESchema Schematic File Version 4 2 | EELAYER 30 0 3 | EELAYER END 4 | $Descr A4 11693 8268 5 | encoding utf-8 6 | Sheet 1 1 7 | Title "" 8 | Date "" 9 | Rev "" 10 | Comp "" 11 | Comment1 "" 12 | Comment2 "" 13 | Comment3 "" 14 | Comment4 "" 15 | $EndDescr 16 | $Comp 17 | L Connector:Conn_01x14_Female J2 18 | U 1 1 5FCC0130 19 | P 6850 3450 20 | F 0 "J2" H 6878 3426 50 0000 L CNN 21 | F 1 "Conn_01x14_Female" H 6878 3335 50 0000 L CNN 22 | F 2 "Connector_PinHeader_2.54mm:PinHeader_1x14_P2.54mm_Vertical" H 6850 3450 50 0001 C CNN 23 | F 3 "~" H 6850 3450 50 0001 C CNN 24 | 1 6850 3450 25 | 1 0 0 -1 26 | $EndComp 27 | $Comp 28 | L Connector_Generic:Conn_02x07_Odd_Even J1 29 | U 1 1 5FCC37ED 30 | P 4750 3500 31 | F 0 "J1" H 4800 4017 50 0000 C CNN 32 | F 1 "Conn_02x07_Odd_Even" H 4800 3926 50 0000 C CNN 33 | F 2 "Connector_IDC:IDC-Header_2x07_P2.54mm_Vertical" H 4750 3500 50 0001 C CNN 34 | F 3 "~" H 4750 3500 50 0001 C CNN 35 | 1 4750 3500 36 | 1 0 0 -1 37 | $EndComp 38 | Wire Wire Line 39 | 6650 3050 6200 3050 40 | Wire Wire Line 41 | 6650 3150 6200 3150 42 | Wire Wire Line 43 | 6650 3250 6200 3250 44 | Wire Wire Line 45 | 6650 3350 6200 3350 46 | Wire Wire Line 47 | 6650 3450 6200 3450 48 | Wire Wire Line 49 | 6650 3550 6200 3550 50 | Wire Wire Line 51 | 6650 3650 6200 3650 52 | Wire Wire Line 53 | 6650 3750 6200 3750 54 | Wire Wire Line 55 | 6650 3850 6200 3850 56 | Wire Wire Line 57 | 6650 3950 6200 3950 58 | Wire Wire Line 59 | 6650 4050 6200 4050 60 | Wire Wire Line 61 | 6650 4150 6200 4150 62 | Wire Wire Line 63 | 5050 3300 5500 3300 64 | Wire Wire Line 65 | 5050 3400 5500 3400 66 | Wire Wire Line 67 | 5050 3500 5500 3500 68 | Wire Wire Line 69 | 5050 3600 5500 3600 70 | Wire Wire Line 71 | 5050 3700 5500 3700 72 | Wire Wire Line 73 | 5050 3800 5500 3800 74 | Wire Wire Line 75 | 4550 3200 4050 3200 76 | Wire Wire Line 77 | 4550 3300 4050 3300 78 | Wire Wire Line 79 | 4550 3400 4050 3400 80 | Wire Wire Line 81 | 4550 3500 4050 3500 82 | Wire Wire Line 83 | 4550 3600 4050 3600 84 | Wire Wire Line 85 | 4550 3700 4050 3700 86 | Wire Wire Line 87 | 4550 3800 4050 3800 88 | $Comp 89 | L power:+5V #PWR0101 90 | U 1 1 5FCD1FCD 91 | P 4050 2950 92 | F 0 "#PWR0101" H 4050 2800 50 0001 C CNN 93 | F 1 "+5V" H 4065 3123 50 0000 C CNN 94 | F 2 "" H 4050 2950 50 0001 C CNN 95 | F 3 "" H 4050 2950 50 0001 C CNN 96 | 1 4050 2950 97 | 1 0 0 -1 98 | $EndComp 99 | $Comp 100 | L power:GND #PWR0102 101 | U 1 1 5FCD36A4 102 | P 5650 3250 103 | F 0 "#PWR0102" H 5650 3000 50 0001 C CNN 104 | F 1 "GND" H 5655 3077 50 0000 C CNN 105 | F 2 "" H 5650 3250 50 0001 C CNN 106 | F 3 "" H 5650 3250 50 0001 C CNN 107 | 1 5650 3250 108 | 1 0 0 -1 109 | $EndComp 110 | Wire Wire Line 111 | 5650 3200 5650 3250 112 | Wire Wire Line 113 | 5050 3200 5650 3200 114 | Wire Wire Line 115 | 4050 2950 4050 3200 116 | $Comp 117 | L power:+5V #PWR0103 118 | U 1 1 5FCD5326 119 | P 6000 2800 120 | F 0 "#PWR0103" H 6000 2650 50 0001 C CNN 121 | F 1 "+5V" H 6015 2973 50 0000 C CNN 122 | F 2 "" H 6000 2800 50 0001 C CNN 123 | F 3 "" H 6000 2800 50 0001 C CNN 124 | 1 6000 2800 125 | 1 0 0 -1 126 | $EndComp 127 | $Comp 128 | L power:GND #PWR0104 129 | U 1 1 5FCD6035 130 | P 6000 3000 131 | F 0 "#PWR0104" H 6000 2750 50 0001 C CNN 132 | F 1 "GND" H 6005 2827 50 0000 C CNN 133 | F 2 "" H 6000 3000 50 0001 C CNN 134 | F 3 "" H 6000 3000 50 0001 C CNN 135 | 1 6000 3000 136 | 1 0 0 -1 137 | $EndComp 138 | Wire Wire Line 139 | 6000 2850 6000 2800 140 | Wire Wire Line 141 | 6000 2850 6650 2850 142 | Wire Wire Line 143 | 6000 2950 6000 3000 144 | Wire Wire Line 145 | 6000 2950 6650 2950 146 | Text Label 4050 3300 0 50 ~ 0 147 | TFT_CS 148 | Text Label 4050 3400 0 50 ~ 0 149 | TFT_DC 150 | Text Label 4050 3500 0 50 ~ 0 151 | SCLK 152 | Text Label 4050 3600 0 50 ~ 0 153 | MISO 154 | Text Label 4050 3700 0 50 ~ 0 155 | TCH_CS 156 | Text Label 4050 3800 0 50 ~ 0 157 | MISO 158 | Text Label 5500 3300 2 50 ~ 0 159 | TFT_RST 160 | Text Label 5500 3400 2 50 ~ 0 161 | MOSI 162 | Text Label 5500 3500 2 50 ~ 0 163 | TFT_LED 164 | Text Label 5500 3600 2 50 ~ 0 165 | SCLK 166 | Text Label 5500 3700 2 50 ~ 0 167 | MOSI 168 | Text Label 5500 3800 2 50 ~ 0 169 | TCH_IRQ 170 | Text Label 6200 3050 0 50 ~ 0 171 | TFT_CS 172 | Text Label 6200 3150 0 50 ~ 0 173 | TFT_RST 174 | Text Label 6200 3250 0 50 ~ 0 175 | TFT_DC 176 | Text Label 6200 3350 0 50 ~ 0 177 | MOSI 178 | Text Label 6200 3450 0 50 ~ 0 179 | SCLK 180 | Text Label 6200 3550 0 50 ~ 0 181 | TFT_LED 182 | Text Label 6200 3650 0 50 ~ 0 183 | MISO 184 | Text Label 6200 3750 0 50 ~ 0 185 | SCLK 186 | Text Label 6200 3850 0 50 ~ 0 187 | TCH_CS 188 | Text Label 6200 3950 0 50 ~ 0 189 | MOSI 190 | Text Label 6200 4050 0 50 ~ 0 191 | MISO 192 | Text Label 6200 4150 0 50 ~ 0 193 | TCH_IRQ 194 | $EndSCHEMATC 195 | -------------------------------------------------------------------------------- /kicad/esp-gps-ntp/esp-gps-ntp-cache.lib: -------------------------------------------------------------------------------- 1 | EESchema-LIBRARY Version 2.4 2 | #encoding utf-8 3 | # 4 | # Connector_Barrel_Jack_Switch 5 | # 6 | DEF Connector_Barrel_Jack_Switch J 0 20 Y N 1 F N 7 | F0 "J" 0 210 50 H V C CNN 8 | F1 "Connector_Barrel_Jack_Switch" 0 -200 50 H V C CNN 9 | F2 "" 50 -40 50 H I C CNN 10 | F3 "" 50 -40 50 H I C CNN 11 | $FPLIST 12 | BarrelJack* 13 | $ENDFPLIST 14 | DRAW 15 | A -130 100 25 901 -901 0 1 10 F -130 125 -130 75 16 | A -130 100 25 901 -901 0 1 10 N -130 125 -130 75 17 | S -200 150 200 -150 0 1 10 f 18 | S 145 125 -130 75 0 1 10 F 19 | P 2 0 1 10 50 -90 75 -65 N 20 | P 2 0 1 10 200 100 150 100 N 21 | P 4 0 1 10 200 0 50 0 50 -90 25 -65 N 22 | P 6 0 1 10 -150 -100 -100 -100 -50 -50 0 -100 100 -100 200 -100 N 23 | X ~ 1 300 100 100 L 50 50 1 1 P 24 | X ~ 2 300 -100 100 L 50 50 1 1 P 25 | X ~ 3 300 0 100 L 50 50 1 1 P 26 | ENDDRAW 27 | ENDDEF 28 | # 29 | # Connector_Conn_01x05_Female 30 | # 31 | DEF Connector_Conn_01x05_Female J 0 40 Y N 1 F N 32 | F0 "J" 0 300 50 H V C CNN 33 | F1 "Connector_Conn_01x05_Female" 0 -300 50 H V C CNN 34 | F2 "" 0 0 50 H I C CNN 35 | F3 "" 0 0 50 H I C CNN 36 | $FPLIST 37 | Connector*:*_1x??_* 38 | $ENDFPLIST 39 | DRAW 40 | A 0 -200 20 901 -901 1 1 6 N 0 -180 0 -220 41 | A 0 -100 20 901 -901 1 1 6 N 0 -80 0 -120 42 | A 0 0 20 901 -901 1 1 6 N 0 20 0 -20 43 | A 0 100 20 901 -901 1 1 6 N 0 120 0 80 44 | A 0 200 20 901 -901 1 1 6 N 0 220 0 180 45 | P 2 1 1 6 -50 -200 -20 -200 N 46 | P 2 1 1 6 -50 -100 -20 -100 N 47 | P 2 1 1 6 -50 0 -20 0 N 48 | P 2 1 1 6 -50 100 -20 100 N 49 | P 2 1 1 6 -50 200 -20 200 N 50 | X Pin_1 1 -200 200 150 R 50 50 1 1 P 51 | X Pin_2 2 -200 100 150 R 50 50 1 1 P 52 | X Pin_3 3 -200 0 150 R 50 50 1 1 P 53 | X Pin_4 4 -200 -100 150 R 50 50 1 1 P 54 | X Pin_5 5 -200 -200 150 R 50 50 1 1 P 55 | ENDDRAW 56 | ENDDEF 57 | # 58 | # Connector_Conn_01x09_Female 59 | # 60 | DEF Connector_Conn_01x09_Female J 0 40 Y N 1 F N 61 | F0 "J" 0 500 50 H V C CNN 62 | F1 "Connector_Conn_01x09_Female" 0 -500 50 H V C CNN 63 | F2 "" 0 0 50 H I C CNN 64 | F3 "" 0 0 50 H I C CNN 65 | $FPLIST 66 | Connector*:*_1x??_* 67 | $ENDFPLIST 68 | DRAW 69 | A 0 -400 20 901 -901 1 1 6 N 0 -380 0 -420 70 | A 0 -300 20 901 -901 1 1 6 N 0 -280 0 -320 71 | A 0 -200 20 901 -901 1 1 6 N 0 -180 0 -220 72 | A 0 -100 20 901 -901 1 1 6 N 0 -80 0 -120 73 | A 0 0 20 901 -901 1 1 6 N 0 20 0 -20 74 | A 0 100 20 901 -901 1 1 6 N 0 120 0 80 75 | A 0 200 20 901 -901 1 1 6 N 0 220 0 180 76 | A 0 300 20 901 -901 1 1 6 N 0 320 0 280 77 | A 0 400 20 901 -901 1 1 6 N 0 420 0 380 78 | P 2 1 1 6 -50 -400 -20 -400 N 79 | P 2 1 1 6 -50 -300 -20 -300 N 80 | P 2 1 1 6 -50 -200 -20 -200 N 81 | P 2 1 1 6 -50 -100 -20 -100 N 82 | P 2 1 1 6 -50 0 -20 0 N 83 | P 2 1 1 6 -50 100 -20 100 N 84 | P 2 1 1 6 -50 200 -20 200 N 85 | P 2 1 1 6 -50 300 -20 300 N 86 | P 2 1 1 6 -50 400 -20 400 N 87 | X Pin_1 1 -200 400 150 R 50 50 1 1 P 88 | X Pin_2 2 -200 300 150 R 50 50 1 1 P 89 | X Pin_3 3 -200 200 150 R 50 50 1 1 P 90 | X Pin_4 4 -200 100 150 R 50 50 1 1 P 91 | X Pin_5 5 -200 0 150 R 50 50 1 1 P 92 | X Pin_6 6 -200 -100 150 R 50 50 1 1 P 93 | X Pin_7 7 -200 -200 150 R 50 50 1 1 P 94 | X Pin_8 8 -200 -300 150 R 50 50 1 1 P 95 | X Pin_9 9 -200 -400 150 R 50 50 1 1 P 96 | ENDDRAW 97 | ENDDEF 98 | # 99 | # Connector_Generic_Conn_02x03_Odd_Even 100 | # 101 | DEF Connector_Generic_Conn_02x03_Odd_Even J 0 40 Y N 1 F N 102 | F0 "J" 50 200 50 H V C CNN 103 | F1 "Connector_Generic_Conn_02x03_Odd_Even" 50 -200 50 H V C CNN 104 | F2 "" 0 0 50 H I C CNN 105 | F3 "" 0 0 50 H I C CNN 106 | $FPLIST 107 | Connector*:*_2x??_* 108 | $ENDFPLIST 109 | DRAW 110 | S -50 -95 0 -105 1 1 6 N 111 | S -50 5 0 -5 1 1 6 N 112 | S -50 105 0 95 1 1 6 N 113 | S -50 150 150 -150 1 1 10 f 114 | S 150 -95 100 -105 1 1 6 N 115 | S 150 5 100 -5 1 1 6 N 116 | S 150 105 100 95 1 1 6 N 117 | X Pin_1 1 -200 100 150 R 50 50 1 1 P 118 | X Pin_2 2 300 100 150 L 50 50 1 1 P 119 | X Pin_3 3 -200 0 150 R 50 50 1 1 P 120 | X Pin_4 4 300 0 150 L 50 50 1 1 P 121 | X Pin_5 5 -200 -100 150 R 50 50 1 1 P 122 | X Pin_6 6 300 -100 150 L 50 50 1 1 P 123 | ENDDRAW 124 | ENDDEF 125 | # 126 | # Connector_Generic_Conn_02x05_Odd_Even 127 | # 128 | DEF Connector_Generic_Conn_02x05_Odd_Even J 0 40 Y N 1 F N 129 | F0 "J" 50 300 50 H V C CNN 130 | F1 "Connector_Generic_Conn_02x05_Odd_Even" 50 -300 50 H V C CNN 131 | F2 "" 0 0 50 H I C CNN 132 | F3 "" 0 0 50 H I C CNN 133 | $FPLIST 134 | Connector*:*_2x??_* 135 | $ENDFPLIST 136 | DRAW 137 | S -50 -195 0 -205 1 1 6 N 138 | S -50 -95 0 -105 1 1 6 N 139 | S -50 5 0 -5 1 1 6 N 140 | S -50 105 0 95 1 1 6 N 141 | S -50 205 0 195 1 1 6 N 142 | S -50 250 150 -250 1 1 10 f 143 | S 150 -195 100 -205 1 1 6 N 144 | S 150 -95 100 -105 1 1 6 N 145 | S 150 5 100 -5 1 1 6 N 146 | S 150 105 100 95 1 1 6 N 147 | S 150 205 100 195 1 1 6 N 148 | X Pin_1 1 -200 200 150 R 50 50 1 1 P 149 | X Pin_10 10 300 -200 150 L 50 50 1 1 P 150 | X Pin_2 2 300 200 150 L 50 50 1 1 P 151 | X Pin_3 3 -200 100 150 R 50 50 1 1 P 152 | X Pin_4 4 300 100 150 L 50 50 1 1 P 153 | X Pin_5 5 -200 0 150 R 50 50 1 1 P 154 | X Pin_6 6 300 0 150 L 50 50 1 1 P 155 | X Pin_7 7 -200 -100 150 R 50 50 1 1 P 156 | X Pin_8 8 300 -100 150 L 50 50 1 1 P 157 | X Pin_9 9 -200 -200 150 R 50 50 1 1 P 158 | ENDDRAW 159 | ENDDEF 160 | # 161 | # Connector_Generic_Conn_02x07_Odd_Even 162 | # 163 | DEF Connector_Generic_Conn_02x07_Odd_Even J 0 40 Y N 1 F N 164 | F0 "J" 50 400 50 H V C CNN 165 | F1 "Connector_Generic_Conn_02x07_Odd_Even" 50 -400 50 H V C CNN 166 | F2 "" 0 0 50 H I C CNN 167 | F3 "" 0 0 50 H I C CNN 168 | $FPLIST 169 | Connector*:*_2x??_* 170 | $ENDFPLIST 171 | DRAW 172 | S -50 -295 0 -305 1 1 6 N 173 | S -50 -195 0 -205 1 1 6 N 174 | S -50 -95 0 -105 1 1 6 N 175 | S -50 5 0 -5 1 1 6 N 176 | S -50 105 0 95 1 1 6 N 177 | S -50 205 0 195 1 1 6 N 178 | S -50 305 0 295 1 1 6 N 179 | S -50 350 150 -350 1 1 10 f 180 | S 150 -295 100 -305 1 1 6 N 181 | S 150 -195 100 -205 1 1 6 N 182 | S 150 -95 100 -105 1 1 6 N 183 | S 150 5 100 -5 1 1 6 N 184 | S 150 105 100 95 1 1 6 N 185 | S 150 205 100 195 1 1 6 N 186 | S 150 305 100 295 1 1 6 N 187 | X Pin_1 1 -200 300 150 R 50 50 1 1 P 188 | X Pin_10 10 300 -100 150 L 50 50 1 1 P 189 | X Pin_11 11 -200 -200 150 R 50 50 1 1 P 190 | X Pin_12 12 300 -200 150 L 50 50 1 1 P 191 | X Pin_13 13 -200 -300 150 R 50 50 1 1 P 192 | X Pin_14 14 300 -300 150 L 50 50 1 1 P 193 | X Pin_2 2 300 300 150 L 50 50 1 1 P 194 | X Pin_3 3 -200 200 150 R 50 50 1 1 P 195 | X Pin_4 4 300 200 150 L 50 50 1 1 P 196 | X Pin_5 5 -200 100 150 R 50 50 1 1 P 197 | X Pin_6 6 300 100 150 L 50 50 1 1 P 198 | X Pin_7 7 -200 0 150 R 50 50 1 1 P 199 | X Pin_8 8 300 0 150 L 50 50 1 1 P 200 | X Pin_9 9 -200 -100 150 R 50 50 1 1 P 201 | ENDDRAW 202 | ENDDEF 203 | # 204 | # Connector_TestPoint 205 | # 206 | DEF Connector_TestPoint TP 0 30 N N 1 F N 207 | F0 "TP" 0 270 50 H V C CNN 208 | F1 "Connector_TestPoint" 0 200 50 H V C CNN 209 | F2 "" 200 0 50 H I C CNN 210 | F3 "" 200 0 50 H I C CNN 211 | $FPLIST 212 | Pin* 213 | Test* 214 | $ENDFPLIST 215 | DRAW 216 | C 0 130 30 0 1 0 N 217 | X 1 1 0 0 100 U 50 50 1 1 P 218 | ENDDRAW 219 | ENDDEF 220 | # 221 | # Device_Battery_Cell 222 | # 223 | DEF Device_Battery_Cell BT 0 0 N N 1 F N 224 | F0 "BT" 100 100 50 H V L CNN 225 | F1 "Device_Battery_Cell" 100 0 50 H V L CNN 226 | F2 "" 0 60 50 V I C CNN 227 | F3 "" 0 60 50 V I C CNN 228 | DRAW 229 | S -90 70 90 60 0 1 0 F 230 | S -62 47 58 27 0 1 0 F 231 | P 2 0 1 0 0 30 0 0 N 232 | P 2 0 1 0 0 70 0 100 N 233 | P 2 0 1 10 20 135 60 135 N 234 | P 2 0 1 10 40 155 40 115 N 235 | X + 1 0 200 100 D 50 50 1 1 P 236 | X - 2 0 -100 100 U 50 50 1 1 P 237 | ENDDRAW 238 | ENDDEF 239 | # 240 | # Device_C_Small 241 | # 242 | DEF Device_C_Small C 0 10 N N 1 F N 243 | F0 "C" 10 70 50 H V L CNN 244 | F1 "Device_C_Small" 10 -80 50 H V L CNN 245 | F2 "" 0 0 50 H I C CNN 246 | F3 "" 0 0 50 H I C CNN 247 | $FPLIST 248 | C_* 249 | $ENDFPLIST 250 | DRAW 251 | P 2 0 1 13 -60 -20 60 -20 N 252 | P 2 0 1 12 -60 20 60 20 N 253 | X ~ 1 0 100 80 D 50 50 1 1 P 254 | X ~ 2 0 -100 80 U 50 50 1 1 P 255 | ENDDRAW 256 | ENDDEF 257 | # 258 | # Device_R 259 | # 260 | DEF Device_R R 0 0 N Y 1 F N 261 | F0 "R" 80 0 50 V V C CNN 262 | F1 "Device_R" 0 0 50 V V C CNN 263 | F2 "" -70 0 50 V I C CNN 264 | F3 "" 0 0 50 H I C CNN 265 | $FPLIST 266 | R_* 267 | $ENDFPLIST 268 | DRAW 269 | S -40 -100 40 100 0 1 10 N 270 | X ~ 1 0 150 50 D 50 50 1 1 P 271 | X ~ 2 0 -150 50 U 50 50 1 1 P 272 | ENDDRAW 273 | ENDDEF 274 | # 275 | # MCP1825_MCP1825 276 | # 277 | DEF MCP1825_MCP1825 U 0 40 Y Y 1 F N 278 | F0 "U" 0 350 50 H V C CNN 279 | F1 "MCP1825_MCP1825" 0 250 50 H V C CNN 280 | F2 "" -400 400 50 H I C CNN 281 | F3 "" -400 400 50 H I C CNN 282 | DRAW 283 | S -350 200 350 -200 0 1 0 f 284 | X SHDN 1 -450 100 100 R 50 50 1 1 I I 285 | X VIN 2 -450 -100 100 R 50 50 1 1 W 286 | X GND 3 0 -300 100 U 50 50 1 1 W 287 | X VOUT 4 450 -100 100 L 50 50 1 1 w 288 | X PWRGD 5 450 100 100 L 50 50 1 1 C 289 | X GND 6 0 -300 100 U 50 50 1 1 W N 290 | ENDDRAW 291 | ENDDEF 292 | # 293 | # Mechanical_MountingHole 294 | # 295 | DEF Mechanical_MountingHole H 0 40 Y Y 1 F N 296 | F0 "H" 0 200 50 H V C CNN 297 | F1 "Mechanical_MountingHole" 0 125 50 H V C CNN 298 | F2 "" 0 0 50 H I C CNN 299 | F3 "" 0 0 50 H I C CNN 300 | $FPLIST 301 | MountingHole* 302 | $ENDFPLIST 303 | DRAW 304 | C 0 0 50 0 1 50 N 305 | ENDDRAW 306 | ENDDEF 307 | # 308 | # RF_Module_ESP32-WROOM-32D 309 | # 310 | DEF RF_Module_ESP32-WROOM-32D U 0 20 Y Y 1 F N 311 | F0 "U" -500 1350 50 H V L CNN 312 | F1 "RF_Module_ESP32-WROOM-32D" 50 1350 50 H V L CNN 313 | F2 "RF_Module:ESP32-WROOM-32" 0 -1500 50 H I C CNN 314 | F3 "" -300 50 50 H I C CNN 315 | ALIAS ESP32-WROOM-32D 316 | $FPLIST 317 | ESP32?WROOM?32* 318 | $ENDFPLIST 319 | DRAW 320 | S -500 1300 500 -1300 0 1 10 f 321 | X GND 1 0 -1400 100 U 50 50 1 1 W 322 | X IO25 10 600 -500 100 L 50 50 1 1 B 323 | X IO26 11 600 -600 100 L 50 50 1 1 B 324 | X IO27 12 600 -700 100 L 50 50 1 1 B 325 | X IO14 13 600 400 100 L 50 50 1 1 B 326 | X IO12 14 600 600 100 L 50 50 1 1 B 327 | X GND 15 0 -1400 100 U 50 50 1 1 P N 328 | X IO13 16 600 500 100 L 50 50 1 1 B 329 | X SHD/SD2 17 -600 -200 100 R 50 50 1 1 B 330 | X SWP/SD3 18 -600 -300 100 R 50 50 1 1 B 331 | X SCS/CMD 19 -600 -500 100 R 50 50 1 1 B 332 | X VDD 2 0 1400 100 D 50 50 1 1 W 333 | X SCK/CLK 20 -600 -400 100 R 50 50 1 1 B 334 | X SDO/SD0 21 -600 0 100 R 50 50 1 1 B 335 | X SDI/SD1 22 -600 -100 100 R 50 50 1 1 B 336 | X IO15 23 600 300 100 L 50 50 1 1 B 337 | X IO2 24 600 1000 100 L 50 50 1 1 B 338 | X IO0 25 600 1200 100 L 50 50 1 1 B 339 | X IO4 26 600 800 100 L 50 50 1 1 B 340 | X IO16 27 600 200 100 L 50 50 1 1 B 341 | X IO17 28 600 100 100 L 50 50 1 1 B 342 | X IO5 29 600 700 100 L 50 50 1 1 B 343 | X EN 3 -600 1200 100 R 50 50 1 1 I 344 | X IO18 30 600 0 100 L 50 50 1 1 B 345 | X IO19 31 600 -100 100 L 50 50 1 1 B 346 | X NC 32 -500 -1100 100 R 50 50 1 1 N N 347 | X IO21 33 600 -200 100 L 50 50 1 1 B 348 | X RXD0/IO3 34 600 900 100 L 50 50 1 1 B 349 | X TXD0/IO1 35 600 1100 100 L 50 50 1 1 B 350 | X IO22 36 600 -300 100 L 50 50 1 1 B 351 | X IO23 37 600 -400 100 L 50 50 1 1 B 352 | X GND 38 0 -1400 100 U 50 50 1 1 P N 353 | X GND 39 0 -1400 100 U 50 50 1 1 P N 354 | X SENSOR_VP 4 -600 1000 100 R 50 50 1 1 I 355 | X SENSOR_VN 5 -600 900 100 R 50 50 1 1 I 356 | X IO34 6 600 -1000 100 L 50 50 1 1 I 357 | X IO35 7 600 -1100 100 L 50 50 1 1 I 358 | X IO32 8 600 -800 100 L 50 50 1 1 B 359 | X IO33 9 600 -900 100 L 50 50 1 1 B 360 | ENDDRAW 361 | ENDDEF 362 | # 363 | # Switch_SW_SPST 364 | # 365 | DEF Switch_SW_SPST SW 0 0 Y N 1 F N 366 | F0 "SW" 0 125 50 H V C CNN 367 | F1 "Switch_SW_SPST" 0 -100 50 H V C CNN 368 | F2 "" 0 0 50 H I C CNN 369 | F3 "" 0 0 50 H I C CNN 370 | DRAW 371 | C -80 0 20 0 0 0 N 372 | C 80 0 20 0 0 0 N 373 | P 2 0 0 0 -60 10 60 70 N 374 | X A 1 -200 0 100 R 50 50 1 1 P 375 | X B 2 200 0 100 L 50 50 1 1 P 376 | ENDDRAW 377 | ENDDEF 378 | # 379 | # Timer_RTC_DS3231M 380 | # 381 | DEF Timer_RTC_DS3231M U 0 20 Y Y 1 F N 382 | F0 "U" -300 350 50 H V R CNN 383 | F1 "Timer_RTC_DS3231M" 400 350 50 H V R CNN 384 | F2 "Package_SO:SOIC-16W_7.5x10.3mm_P1.27mm" 0 -600 50 H I C CNN 385 | F3 "" 270 50 50 H I C CNN 386 | $FPLIST 387 | SOIC*7.5x10.3mm*P1.27mm* 388 | $ENDFPLIST 389 | DRAW 390 | S -400 300 400 -300 0 1 10 f 391 | X 32KHZ 1 500 200 100 L 50 50 1 1 C 392 | X GND 10 0 -400 100 U 50 50 1 1 P N 393 | X GND 11 0 -400 100 U 50 50 1 1 P N 394 | X GND 12 0 -400 100 U 50 50 1 1 P N 395 | X GND 13 0 -400 100 U 50 50 1 1 W 396 | X VBAT 14 0 400 100 D 50 50 1 1 W 397 | X SDA 15 -500 100 100 R 50 50 1 1 B 398 | X SCL 16 -500 200 100 R 50 50 1 1 I 399 | X VCC 2 -100 400 100 D 50 50 1 1 W 400 | X ~INT~/SQW 3 500 -100 100 L 50 50 1 1 C 401 | X ~RST 4 -500 -200 100 R 50 50 1 1 B 402 | X GND 5 0 -400 100 U 50 50 1 1 P N 403 | X GND 6 0 -400 100 U 50 50 1 1 P N 404 | X GND 7 0 -400 100 U 50 50 1 1 P N 405 | X GND 8 0 -400 100 U 50 50 1 1 P N 406 | X GND 9 0 -400 100 U 50 50 1 1 P N 407 | ENDDRAW 408 | ENDDEF 409 | # 410 | # power_+3V3 411 | # 412 | DEF power_+3V3 #PWR 0 0 Y Y 1 F P 413 | F0 "#PWR" 0 -150 50 H I C CNN 414 | F1 "power_+3V3" 0 140 50 H V C CNN 415 | F2 "" 0 0 50 H I C CNN 416 | F3 "" 0 0 50 H I C CNN 417 | ALIAS +3.3V 418 | DRAW 419 | P 2 0 1 0 -30 50 0 100 N 420 | P 2 0 1 0 0 0 0 100 N 421 | P 2 0 1 0 0 100 30 50 N 422 | X +3V3 1 0 0 0 U 50 50 1 1 W N 423 | ENDDRAW 424 | ENDDEF 425 | # 426 | # power_+5V 427 | # 428 | DEF power_+5V #PWR 0 0 Y Y 1 F P 429 | F0 "#PWR" 0 -150 50 H I C CNN 430 | F1 "power_+5V" 0 140 50 H V C CNN 431 | F2 "" 0 0 50 H I C CNN 432 | F3 "" 0 0 50 H I C CNN 433 | DRAW 434 | P 2 0 1 0 -30 50 0 100 N 435 | P 2 0 1 0 0 0 0 100 N 436 | P 2 0 1 0 0 100 30 50 N 437 | X +5V 1 0 0 0 U 50 50 1 1 W N 438 | ENDDRAW 439 | ENDDEF 440 | # 441 | # power_GND 442 | # 443 | DEF power_GND #PWR 0 0 Y Y 1 F P 444 | F0 "#PWR" 0 -250 50 H I C CNN 445 | F1 "power_GND" 0 -150 50 H V C CNN 446 | F2 "" 0 0 50 H I C CNN 447 | F3 "" 0 0 50 H I C CNN 448 | DRAW 449 | P 6 0 1 0 0 0 0 -50 50 -50 0 -100 -50 -50 0 -50 N 450 | X GND 1 0 0 0 D 50 50 1 1 W N 451 | ENDDRAW 452 | ENDDEF 453 | # 454 | #End Library 455 | -------------------------------------------------------------------------------- /kicad/esp-gps-ntp/esp-gps-ntp.pro: -------------------------------------------------------------------------------- 1 | update=Saturday, December 26, 2020 at 12:27:50 PM 2 | version=1 3 | last_client=kicad 4 | [general] 5 | version=1 6 | RootSch= 7 | BoardNm= 8 | [cvpcb] 9 | version=1 10 | NetIExt=net 11 | [eeschema] 12 | version=1 13 | LibDir= 14 | [eeschema/libraries] 15 | [pcbnew] 16 | version=1 17 | PageLayoutDescrFile= 18 | LastNetListRead=esp-gps-ntp.net 19 | CopperLayerCount=2 20 | BoardThickness=1.6 21 | AllowMicroVias=0 22 | AllowBlindVias=0 23 | RequireCourtyardDefinitions=1 24 | ProhibitOverlappingCourtyards=1 25 | MinTrackWidth=0.16 26 | MinViaDiameter=0.6 27 | MinViaDrill=0.3 28 | MinMicroViaDiameter=0.2 29 | MinMicroViaDrill=0.09999999999999999 30 | MinHoleToHole=0.25 31 | TrackWidth1=0.254 32 | ViaDiameter1=0.8 33 | ViaDrill1=0.4 34 | dPairWidth1=0.2 35 | dPairGap1=0.25 36 | dPairViaGap1=0.25 37 | SilkLineWidth=0.12 38 | SilkTextSizeV=1 39 | SilkTextSizeH=1 40 | SilkTextSizeThickness=0.15 41 | SilkTextItalic=0 42 | SilkTextUpright=1 43 | CopperLineWidth=0.2 44 | CopperTextSizeV=1.5 45 | CopperTextSizeH=1.5 46 | CopperTextThickness=0.3 47 | CopperTextItalic=0 48 | CopperTextUpright=1 49 | EdgeCutLineWidth=0.05 50 | CourtyardLineWidth=0.05 51 | OthersLineWidth=0.15 52 | OthersTextSizeV=1 53 | OthersTextSizeH=1 54 | OthersTextSizeThickness=0.15 55 | OthersTextItalic=0 56 | OthersTextUpright=1 57 | SolderMaskClearance=0 58 | SolderMaskMinWidth=0 59 | SolderPasteClearance=0 60 | SolderPasteRatio=-0 61 | [pcbnew/Layer.F.Cu] 62 | Name=F.Cu 63 | Type=0 64 | Enabled=1 65 | [pcbnew/Layer.In1.Cu] 66 | Name=In1.Cu 67 | Type=0 68 | Enabled=0 69 | [pcbnew/Layer.In2.Cu] 70 | Name=In2.Cu 71 | Type=0 72 | Enabled=0 73 | [pcbnew/Layer.In3.Cu] 74 | Name=In3.Cu 75 | Type=0 76 | Enabled=0 77 | [pcbnew/Layer.In4.Cu] 78 | Name=In4.Cu 79 | Type=0 80 | Enabled=0 81 | [pcbnew/Layer.In5.Cu] 82 | Name=In5.Cu 83 | Type=0 84 | Enabled=0 85 | [pcbnew/Layer.In6.Cu] 86 | Name=In6.Cu 87 | Type=0 88 | Enabled=0 89 | [pcbnew/Layer.In7.Cu] 90 | Name=In7.Cu 91 | Type=0 92 | Enabled=0 93 | [pcbnew/Layer.In8.Cu] 94 | Name=In8.Cu 95 | Type=0 96 | Enabled=0 97 | [pcbnew/Layer.In9.Cu] 98 | Name=In9.Cu 99 | Type=0 100 | Enabled=0 101 | [pcbnew/Layer.In10.Cu] 102 | Name=In10.Cu 103 | Type=0 104 | Enabled=0 105 | [pcbnew/Layer.In11.Cu] 106 | Name=In11.Cu 107 | Type=0 108 | Enabled=0 109 | [pcbnew/Layer.In12.Cu] 110 | Name=In12.Cu 111 | Type=0 112 | Enabled=0 113 | [pcbnew/Layer.In13.Cu] 114 | Name=In13.Cu 115 | Type=0 116 | Enabled=0 117 | [pcbnew/Layer.In14.Cu] 118 | Name=In14.Cu 119 | Type=0 120 | Enabled=0 121 | [pcbnew/Layer.In15.Cu] 122 | Name=In15.Cu 123 | Type=0 124 | Enabled=0 125 | [pcbnew/Layer.In16.Cu] 126 | Name=In16.Cu 127 | Type=0 128 | Enabled=0 129 | [pcbnew/Layer.In17.Cu] 130 | Name=In17.Cu 131 | Type=0 132 | Enabled=0 133 | [pcbnew/Layer.In18.Cu] 134 | Name=In18.Cu 135 | Type=0 136 | Enabled=0 137 | [pcbnew/Layer.In19.Cu] 138 | Name=In19.Cu 139 | Type=0 140 | Enabled=0 141 | [pcbnew/Layer.In20.Cu] 142 | Name=In20.Cu 143 | Type=0 144 | Enabled=0 145 | [pcbnew/Layer.In21.Cu] 146 | Name=In21.Cu 147 | Type=0 148 | Enabled=0 149 | [pcbnew/Layer.In22.Cu] 150 | Name=In22.Cu 151 | Type=0 152 | Enabled=0 153 | [pcbnew/Layer.In23.Cu] 154 | Name=In23.Cu 155 | Type=0 156 | Enabled=0 157 | [pcbnew/Layer.In24.Cu] 158 | Name=In24.Cu 159 | Type=0 160 | Enabled=0 161 | [pcbnew/Layer.In25.Cu] 162 | Name=In25.Cu 163 | Type=0 164 | Enabled=0 165 | [pcbnew/Layer.In26.Cu] 166 | Name=In26.Cu 167 | Type=0 168 | Enabled=0 169 | [pcbnew/Layer.In27.Cu] 170 | Name=In27.Cu 171 | Type=0 172 | Enabled=0 173 | [pcbnew/Layer.In28.Cu] 174 | Name=In28.Cu 175 | Type=0 176 | Enabled=0 177 | [pcbnew/Layer.In29.Cu] 178 | Name=In29.Cu 179 | Type=0 180 | Enabled=0 181 | [pcbnew/Layer.In30.Cu] 182 | Name=In30.Cu 183 | Type=0 184 | Enabled=0 185 | [pcbnew/Layer.B.Cu] 186 | Name=B.Cu 187 | Type=0 188 | Enabled=1 189 | [pcbnew/Layer.B.Adhes] 190 | Enabled=1 191 | [pcbnew/Layer.F.Adhes] 192 | Enabled=1 193 | [pcbnew/Layer.B.Paste] 194 | Enabled=1 195 | [pcbnew/Layer.F.Paste] 196 | Enabled=1 197 | [pcbnew/Layer.B.SilkS] 198 | Enabled=1 199 | [pcbnew/Layer.F.SilkS] 200 | Enabled=1 201 | [pcbnew/Layer.B.Mask] 202 | Enabled=1 203 | [pcbnew/Layer.F.Mask] 204 | Enabled=1 205 | [pcbnew/Layer.Dwgs.User] 206 | Enabled=1 207 | [pcbnew/Layer.Cmts.User] 208 | Enabled=1 209 | [pcbnew/Layer.Eco1.User] 210 | Enabled=1 211 | [pcbnew/Layer.Eco2.User] 212 | Enabled=1 213 | [pcbnew/Layer.Edge.Cuts] 214 | Enabled=1 215 | [pcbnew/Layer.Margin] 216 | Enabled=1 217 | [pcbnew/Layer.B.CrtYd] 218 | Enabled=1 219 | [pcbnew/Layer.F.CrtYd] 220 | Enabled=1 221 | [pcbnew/Layer.B.Fab] 222 | Enabled=1 223 | [pcbnew/Layer.F.Fab] 224 | Enabled=1 225 | [pcbnew/Layer.Rescue] 226 | Enabled=0 227 | [pcbnew/Netclasses] 228 | [pcbnew/Netclasses/Default] 229 | Name=Default 230 | Clearance=0.3 231 | TrackWidth=0.254 232 | ViaDiameter=0.8 233 | ViaDrill=0.4 234 | uViaDiameter=0.3 235 | uViaDrill=0.1 236 | dPairWidth=0.2 237 | dPairGap=0.25 238 | dPairViaGap=0.25 239 | [pcbnew/Netclasses/1] 240 | Name=Power 241 | Clearance=0.2 242 | TrackWidth=0.75 243 | ViaDiameter=0.8 244 | ViaDrill=0.4 245 | uViaDiameter=0.3 246 | uViaDrill=0.1 247 | dPairWidth=0.2 248 | dPairGap=0.25 249 | dPairViaGap=0.25 250 | [schematic_editor] 251 | version=1 252 | PageLayoutDescrFile= 253 | PlotDirectoryName= 254 | SubpartIdSeparator=0 255 | SubpartFirstId=65 256 | NetFmtName=Pcbnew 257 | SpiceAjustPassiveValues=0 258 | LabSize=50 259 | ERC_TestSimilarLabels=1 260 | -------------------------------------------------------------------------------- /main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | file (GLOB SOURCES *.cpp highint5.S) 3 | idf_component_register(SRCS ${SOURCES} 4 | INCLUDE_DIRS . 5 | REQUIRES lvgl_esp32_drivers lvgl lvgl_tft lvgl_touch lvgl_cpp minmea nvs_flash) 6 | component_compile_options(-std=c++17) 7 | target_link_libraries(${COMPONENT_TARGET} "-u ld_include_my_isr_file") 8 | -------------------------------------------------------------------------------- /main/Config.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "Config.h" 26 | #include "esp_log.h" 27 | #include "string.h" 28 | 29 | static const char* TAG = "Config"; 30 | static const char* KEY_WIFI_SSID = "wifi_ssid"; 31 | static const char* KEY_WIFI_PASS = "wifi_pass"; 32 | static const char* KEY_BIAS = "bias"; 33 | static const char* KEY_TARGET = "target"; 34 | 35 | Config::Config() 36 | { 37 | setWiFiSSID(""); 38 | setWiFiPassword(""); 39 | } 40 | 41 | Config::~Config() 42 | { 43 | } 44 | 45 | bool Config::begin(const char* partition_name) 46 | { 47 | ESP_LOGI(TAG, "::begin()"); 48 | esp_err_t err = nvs_flash_init_partition(partition_name); 49 | if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) { 50 | // NVS partition was truncated and needs to be erased 51 | // Retry nvs_flash_init 52 | ESP_LOGI(TAG, "call nvs_flash_erase_partition '%s'", partition_name); 53 | err = nvs_flash_erase_partition(partition_name); 54 | if (err != ESP_OK) 55 | { 56 | ESP_LOGE(TAG, "failed nvs_flash_erase_partition '%s' nvs: %d (%s)", partition_name, err, esp_err_to_name(err)); 57 | return false; 58 | } 59 | ESP_LOGI(TAG, "call nvs_flash_init_partition '%s'", partition_name); 60 | err = nvs_flash_init_partition(partition_name); 61 | if (err != ESP_OK) 62 | { 63 | ESP_LOGE(TAG, "failed nvs_flash_init_partition '%s' nvs: %d (%s)", partition_name, err, esp_err_to_name(err)); 64 | return false; 65 | } 66 | } 67 | 68 | ESP_LOGI(TAG, "opening NVS partition '%s'", partition_name); 69 | err = nvs_open(partition_name, NVS_READWRITE, &_nvs); 70 | if (err != ESP_OK) 71 | { 72 | ESP_LOGE(TAG, "failed to open '%s' nvs: %d (%s)", partition_name, err, esp_err_to_name(err)); 73 | return false; 74 | } 75 | return true; 76 | } 77 | 78 | void Config::getString(const char* key, const char** valuep, const char* def_value) 79 | { 80 | if (*valuep) 81 | { 82 | delete[] *valuep; 83 | } 84 | *valuep = def_value; 85 | 86 | size_t len; 87 | esp_err_t err = nvs_get_str(_nvs, key, nullptr, &len); 88 | if (err != ESP_OK) 89 | { 90 | return; 91 | } 92 | char* value = new char[len+1]; 93 | nvs_get_str(_nvs, key, value, &len); 94 | *valuep = value; 95 | } 96 | 97 | char* Config::getString(const char* key, const char* def_value) 98 | { 99 | size_t len; 100 | esp_err_t err = nvs_get_str(_nvs, key, nullptr, &len); 101 | if (err != ESP_OK) 102 | { 103 | return copyString(def_value); 104 | } 105 | char* value = new char[len+1]; 106 | nvs_get_str(_nvs, key, value, &len); 107 | return value; 108 | } 109 | 110 | char* Config::copyString(const char* str) 111 | { 112 | if (str == nullptr) 113 | { 114 | return nullptr; 115 | } 116 | size_t len = strlen(str); 117 | char* value = new char[len+1]; 118 | strcpy(value, str); 119 | return value; 120 | } 121 | 122 | float Config::getFloat(const char* key, float def_value) 123 | { 124 | size_t len; 125 | esp_err_t err = nvs_get_blob(_nvs, key, nullptr, &len); 126 | if (err != ESP_OK) 127 | { 128 | return def_value; 129 | } 130 | 131 | if (len != sizeof(float)) 132 | { 133 | ESP_LOGE(TAG, "wrong size for value of '%s' %d != %d", key, len, sizeof(float)); 134 | return def_value; 135 | } 136 | float value; 137 | nvs_get_blob(_nvs, key, &value, &len); 138 | return value; 139 | } 140 | 141 | bool Config::load() 142 | { 143 | ESP_LOGI(TAG, "::load()"); 144 | if (_wifi_ssid != nullptr) 145 | { 146 | delete[] _wifi_ssid; 147 | } 148 | _wifi_ssid = getString(KEY_WIFI_SSID); 149 | 150 | if (_wifi_pass != nullptr) 151 | { 152 | delete[] _wifi_pass; 153 | } 154 | _wifi_pass = getString(KEY_WIFI_PASS); 155 | 156 | _bias = getFloat(KEY_BIAS); 157 | 158 | _target = getFloat(KEY_TARGET); 159 | 160 | ESP_LOGI(TAG, "::load: ssid=%s pass=%s bias=%0f target=%0f", _wifi_ssid, _wifi_pass, _bias, _target); 161 | return true; 162 | } 163 | 164 | bool Config::save() 165 | { 166 | ESP_LOGI(TAG, "::save()"); 167 | bool ret = true; 168 | esp_err_t err = nvs_set_str(_nvs, KEY_WIFI_SSID, _wifi_ssid); 169 | if (err != ESP_OK) 170 | { 171 | ESP_LOGE(TAG, "::save failed to set '%s=%s': %d (%s)", KEY_WIFI_SSID, _wifi_ssid, err, esp_err_to_name(err)); 172 | ret = false; 173 | } 174 | err = nvs_set_str(_nvs, KEY_WIFI_PASS, _wifi_pass); 175 | if (err != ESP_OK) 176 | { 177 | ESP_LOGE(TAG, "::save failed to set '%s=%s': %d (%s)", KEY_WIFI_PASS, _wifi_pass, err, esp_err_to_name(err)); 178 | ret = false; 179 | } 180 | err = nvs_set_blob(_nvs, KEY_BIAS, &_bias, sizeof(_bias)); 181 | if (err != ESP_OK) 182 | { 183 | ESP_LOGE(TAG, "::save failed to set '%s=%f': %d (%s)", KEY_BIAS, _bias, err, esp_err_to_name(err)); 184 | ret = false; 185 | } 186 | err = nvs_set_blob(_nvs, KEY_TARGET, &_target, sizeof(_target)); 187 | if (err != ESP_OK) 188 | { 189 | ESP_LOGE(TAG, "::save failed to set '%s=%f': %d (%s)", KEY_TARGET, _target, err, esp_err_to_name(err)); 190 | ret = false; 191 | } 192 | return ret; 193 | } 194 | 195 | void Config::setWiFiSSID(const char* ssid) 196 | { 197 | if (_wifi_ssid != nullptr) 198 | { 199 | delete[] _wifi_ssid; 200 | } 201 | _wifi_ssid = copyString(ssid); 202 | } 203 | 204 | const char* Config::getWiFiSSID() 205 | { 206 | if (_wifi_ssid == nullptr) 207 | { 208 | return ""; 209 | } 210 | return _wifi_ssid; 211 | } 212 | 213 | void Config::setWiFiPassword(const char* password) 214 | { 215 | if (_wifi_pass != nullptr) 216 | { 217 | delete[] _wifi_pass; 218 | } 219 | _wifi_pass = copyString(password); 220 | } 221 | 222 | const char* Config::getWiFiPassword() 223 | { 224 | if (_wifi_pass == nullptr) 225 | { 226 | return ""; 227 | } 228 | return _wifi_pass; 229 | } 230 | 231 | void Config::setBias(float bias) 232 | { 233 | _bias = bias; 234 | } 235 | 236 | float Config::getBias() 237 | { 238 | return _bias; 239 | } 240 | 241 | void Config::setTarget(float target) 242 | { 243 | _target = target; 244 | } 245 | 246 | float Config::getTarget() 247 | { 248 | return _target; 249 | } 250 | -------------------------------------------------------------------------------- /main/Config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _CONFIG_H 26 | #define _CONFIG_H 27 | 28 | #include "nvs_flash.h" 29 | #include 30 | 31 | class Config { 32 | public: 33 | Config(); 34 | ~Config(); 35 | bool begin(const char* partition_name = "config"); 36 | 37 | bool load(); 38 | bool save(); 39 | 40 | void setWiFiSSID(const char* ssid); 41 | const char* getWiFiSSID(); 42 | void setWiFiPassword(const char* password); 43 | const char* getWiFiPassword(); 44 | void setBias(float bias); 45 | float getBias(); 46 | void setTarget(float target); 47 | float getTarget(); 48 | private: 49 | nvs_handle_t _nvs; 50 | char* _wifi_ssid = nullptr; 51 | char* _wifi_pass = nullptr; 52 | float _bias = 0.0; 53 | float _target = 10.0; 54 | char* getString(const char* key, const char* def_value = ""); 55 | void getString(const char* key, const char** valuep, const char* def_value = ""); 56 | float getFloat(const char* key, float def_value = 0.0); 57 | char* copyString(const char* str); 58 | }; 59 | 60 | #endif // _CONFIG_H 61 | -------------------------------------------------------------------------------- /main/DS3231.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "DS3231.h" 26 | //#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE 27 | #include "esp_log.h" 28 | 29 | static const char* TAG = "DS3231"; 30 | 31 | static inline uint8_t bcd2dec(uint8_t val) 32 | { 33 | return (val >> 4) * 10 + (val & 0x0f); 34 | } 35 | 36 | static inline uint8_t dec2bcd(uint8_t val) 37 | { 38 | return ((val / 10) << 4) + (val % 10); 39 | } 40 | 41 | DS3231::DS3231(i2c_port_t i2c) : _i2c(i2c) 42 | { 43 | } 44 | 45 | DS3231::~DS3231() 46 | { 47 | } 48 | 49 | bool DS3231::begin() 50 | { 51 | _lock = xSemaphoreCreateBinary(); 52 | xSemaphoreGive(_lock); 53 | uint8_t data[REG_CNT]; 54 | if (!read(SECONDS, REG_CNT, data)) 55 | { 56 | ESP_LOGE(TAG, "failed to read all registers!"); 57 | return false; 58 | } 59 | ESP_LOGI(TAG, "seconds: 0x%02x", data[SECONDS]); 60 | ESP_LOGI(TAG, "minutes: 0x%02x", data[MINUTES]); 61 | ESP_LOGI(TAG, "hours: 0x%02x", data[HOURS]); 62 | ESP_LOGI(TAG, "day: 0x%02x", data[DAY]); 63 | ESP_LOGI(TAG, "date: 0x%02x", data[DATE]); 64 | ESP_LOGI(TAG, "month: 0x%02x", data[MONTH]); 65 | ESP_LOGI(TAG, "year: 0x%02x", data[YEAR]); 66 | ESP_LOGI(TAG, "control: 0x%02x", data[CONTROL]); 67 | ESP_LOGI(TAG, "status: 0x%02x", data[STATUS]); 68 | ESP_LOGI(TAG, "aging: %d", (int8_t)data[AGEOFFSET]); 69 | 70 | _age_offset = (int8_t)data[AGEOFFSET]; 71 | 72 | updateReg(CONTROL, SQWAVE_1HZ, EOSC|BBSQW|SQWAVE_MASK|INTCN); 73 | updateReg(STATUS, 0, OSC_STOP_FLAG); 74 | updateReg(HOURS, 0, DS3231_12HR); 75 | 76 | return true; 77 | } 78 | 79 | bool DS3231::getTime(struct tm *time) 80 | { 81 | uint8_t data[7]; 82 | 83 | if (!read(SECONDS, sizeof(data), data)) 84 | { 85 | ESP_LOGE(TAG, "failed to read time from ds3231!"); 86 | return false; 87 | } 88 | 89 | /* convert to unix time structure */ 90 | time->tm_sec = bcd2dec(data[0]); 91 | time->tm_min = bcd2dec(data[1]); 92 | if (data[2] & DS3231_12HR) 93 | { 94 | /* 12H */ 95 | time->tm_hour = bcd2dec(data[2] & DS3231_12HR_MASK) - 1; 96 | /* AM/PM? */ 97 | if (data[2] & DS3231_PM) time->tm_hour += 12; 98 | } 99 | else time->tm_hour = bcd2dec(data[2]); /* 24H */ 100 | time->tm_wday = bcd2dec(data[3]) - 1; 101 | time->tm_mday = bcd2dec(data[4]); 102 | time->tm_mon = bcd2dec(data[5] & DS3231_MONTH_MASK) - 1; 103 | time->tm_year = bcd2dec(data[6]) + 100; 104 | time->tm_isdst = 0; 105 | 106 | // apply a time zone (if you are not using localtime on the rtc or you want to check/apply DST) 107 | //applyTZ(time); 108 | 109 | return true; 110 | } 111 | 112 | bool DS3231::setTime(struct tm* time) 113 | { 114 | uint8_t data[7]; 115 | 116 | /* time/date data */ 117 | data[0] = dec2bcd(time->tm_sec); 118 | data[1] = dec2bcd(time->tm_min); 119 | data[2] = dec2bcd(time->tm_hour); 120 | /* The week data must be in the range 1 to 7, and to keep the start on the 121 | * same day as for tm_wday have it start at 1 on Sunday. */ 122 | data[3] = dec2bcd(time->tm_wday + 1); 123 | data[4] = dec2bcd(time->tm_mday); 124 | data[5] = dec2bcd(time->tm_mon + 1); 125 | data[6] = dec2bcd(time->tm_year - 100); 126 | bool ret = write(SECONDS, sizeof(data), data); 127 | return ret; 128 | } 129 | 130 | int8_t DS3231::getAgeOffset() 131 | { 132 | return _age_offset; 133 | } 134 | 135 | bool DS3231::setAgeOffset(int8_t ageoff) 136 | { 137 | if (!writeReg(AGEOFFSET, (uint8_t)ageoff)) 138 | { 139 | ESP_LOGE(TAG, "failed to write AGEOFFSET register!"); 140 | return false; 141 | } 142 | 143 | _age_offset = ageoff; 144 | 145 | if (!updateReg(CONTROL, CONV, CONV)) 146 | { 147 | ESP_LOGE(TAG, "failed to start conversion!"); 148 | return false; 149 | } 150 | 151 | return true; 152 | } 153 | 154 | bool DS3231::updateReg(Register reg, uint8_t value, uint8_t mask) 155 | { 156 | ESP_LOGD(TAG, "::updateReg: reg: 0x%02x value: 0x%02x mask: 0x%02x", reg, value, mask); 157 | bool ret = false; 158 | if (xSemaphoreTake( _lock, ( TickType_t ) 1000 ) == pdTRUE) 159 | { 160 | uint8_t old = 0; 161 | if (mask != 0xff) 162 | { 163 | if (!readReg(reg, &old)) 164 | { 165 | xSemaphoreGive(_lock); 166 | return false; 167 | } 168 | value = (value & mask) | (old & ~mask); // keep some old bits 169 | } 170 | ret = writeReg(reg, value); 171 | xSemaphoreGive(_lock); 172 | } 173 | return ret; 174 | } 175 | 176 | bool DS3231::writeReg(Register reg, uint8_t value) 177 | { 178 | ESP_LOGD(TAG, "::writeReg: reg: 0x%02x value: 0x%02x", reg, value); 179 | 180 | i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 181 | i2c_master_start(cmd); 182 | i2c_master_write_byte(cmd, DS3231_ADDR << 1 | I2C_MASTER_WRITE, true); 183 | i2c_master_write_byte(cmd, reg, true); 184 | i2c_master_write_byte(cmd, value, true); 185 | i2c_master_stop(cmd); 186 | esp_err_t err = i2c_master_cmd_begin(_i2c, cmd, 1000 / portTICK_RATE_MS); 187 | i2c_cmd_link_delete(cmd); 188 | if (err != ESP_OK) 189 | { 190 | ESP_LOGE(TAG, "::writeReg i2c master command failed: %d (%s)", err, esp_err_to_name(err)); 191 | return false; 192 | } 193 | 194 | return true; 195 | } 196 | 197 | bool DS3231::write(Register reg, size_t len, uint8_t* value) 198 | { 199 | ESP_LOGD(TAG, "::write: reg: 0x%02x len: %u", reg, len); 200 | 201 | i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 202 | i2c_master_start(cmd); 203 | i2c_master_write_byte(cmd, DS3231_ADDR << 1 | I2C_MASTER_WRITE, true); 204 | i2c_master_write_byte(cmd, reg, true); 205 | i2c_master_write(cmd, value, len, true); 206 | i2c_master_stop(cmd); 207 | esp_err_t err = i2c_master_cmd_begin(_i2c, cmd, 1000 / portTICK_RATE_MS); 208 | i2c_cmd_link_delete(cmd); 209 | if (err != ESP_OK) 210 | { 211 | ESP_LOGE(TAG, "::write i2c master command failed: %d (%s)", err, esp_err_to_name(err)); 212 | return false; 213 | } 214 | 215 | return true; 216 | } 217 | 218 | bool DS3231::readReg(Register reg, uint8_t* value) 219 | { 220 | ESP_LOGD(TAG, "::readReg: reg: 0x%02x", reg); 221 | bool ret = true; 222 | 223 | i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 224 | i2c_master_start(cmd); 225 | i2c_master_write_byte(cmd, DS3231_ADDR << 1 | I2C_MASTER_WRITE, true); 226 | i2c_master_write_byte(cmd, reg, true); 227 | i2c_master_start(cmd); 228 | i2c_master_write_byte(cmd, DS3231_ADDR << 1 | I2C_MASTER_READ, true); 229 | i2c_master_read_byte(cmd, value, I2C_MASTER_NACK); 230 | i2c_master_stop(cmd); 231 | esp_err_t err = i2c_master_cmd_begin(_i2c, cmd, 1000 / portTICK_RATE_MS); 232 | i2c_cmd_link_delete(cmd); 233 | if (err != ESP_OK) 234 | { 235 | ESP_LOGE(TAG, "::readReg i2c master command failed: %d (%s)", err, esp_err_to_name(err)); 236 | return false; 237 | } 238 | 239 | ESP_LOGD(TAG, "::readReg: value: 0x%02x", *value); 240 | return ret; 241 | } 242 | 243 | bool DS3231::read(Register reg, size_t len, uint8_t* value) 244 | { 245 | ESP_LOGD(TAG, "::read: reg: 0x%02x len: %u", reg, len); 246 | bool ret = true; 247 | 248 | i2c_cmd_handle_t cmd = i2c_cmd_link_create(); 249 | i2c_master_start(cmd); 250 | i2c_master_write_byte(cmd, DS3231_ADDR << 1 | I2C_MASTER_WRITE, true); 251 | i2c_master_write_byte(cmd, reg, true); 252 | i2c_master_start(cmd); 253 | i2c_master_write_byte(cmd, DS3231_ADDR << 1 | I2C_MASTER_READ, true); 254 | i2c_master_read(cmd, value, len, I2C_MASTER_LAST_NACK); 255 | i2c_master_stop(cmd); 256 | esp_err_t err = i2c_master_cmd_begin(_i2c, cmd, 1000 / portTICK_RATE_MS); 257 | i2c_cmd_link_delete(cmd); 258 | if (err != ESP_OK) 259 | { 260 | ESP_LOGE(TAG, "::read i2c master command failed: %d (%s)", err, esp_err_to_name(err)); 261 | return false; 262 | } 263 | 264 | return ret; 265 | } 266 | -------------------------------------------------------------------------------- /main/DS3231.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _DS3231_H 26 | #define _DS3231_H 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | class DS3231 33 | { 34 | public: 35 | enum SquareWave 36 | { 37 | SQWAVE_1HZ = 0x00, 38 | SQWAVE_1024HZ = 0x08, 39 | SQWAVE_4096HZ = 0x10, 40 | SQWAVE_8192HZ = 0x18 41 | }; 42 | 43 | DS3231(i2c_port_t i2c = I2C_NUM_0); 44 | virtual ~DS3231(); 45 | bool begin(); 46 | 47 | bool getTime(struct tm* tm); 48 | bool setTime(struct tm* tm); 49 | 50 | int8_t getAgeOffset(); 51 | bool setAgeOffset(int8_t ageoff); 52 | 53 | protected: 54 | const uint8_t DS3231_ADDR = 0x68; 55 | const uint8_t DS3231_12HR = 0x40; 56 | const uint8_t DS3231_PM = 0x20; 57 | const uint8_t DS3231_12HR_MASK = 0x1f; 58 | const uint8_t DS3231_MONTH_MASK = 0x1f; 59 | enum Register : uint8_t 60 | { 61 | SECONDS = 0x00, 62 | MINUTES = 0x01, 63 | HOURS = 0x02, 64 | DAY = 0x03, 65 | DATE = 0x04, 66 | MONTH = 0x05, 67 | YEAR = 0x06, 68 | A1_SECONDS = 0x07, 69 | A1_MINUTES = 0x08, 70 | A1_HOURS = 0x09, 71 | A1_DAYDATE = 0x0a, 72 | A2_MINUTES = 0x0b, 73 | A2_HOURS = 0x0c, 74 | A2_DAYDATE = 0x0d, 75 | CONTROL = 0x0e, 76 | STATUS = 0x0f, 77 | AGEOFFSET = 0x10, 78 | TEMP_MSB = 0x11, 79 | TEMP_LSB = 0x12, 80 | REG_CNT, 81 | }; 82 | 83 | enum Control : uint8_t 84 | { 85 | EOSC = 0x80, 86 | BBSQW = 0x40, 87 | CONV = 0x20, 88 | SQWAVE_MASK = 0x18, 89 | INTCN = 0x04, 90 | ALARM_2_EN = 0x02, 91 | ALARM_1_EN = 0x01 92 | }; 93 | 94 | enum Status : uint8_t 95 | { 96 | OSC_STOP_FLAG = 0x80, 97 | EN32KHZ = 0x08, 98 | BUSY = 0x04, 99 | ALARM_2_FLAG = 0x02, 100 | ALARM_1_FLAG = 0x01 101 | }; 102 | 103 | bool updateReg(Register reg, uint8_t value, uint8_t mask); 104 | bool writeReg(Register reg, uint8_t value); 105 | bool write(Register reg, size_t len, uint8_t* value); 106 | bool readReg(Register reg, uint8_t* value); 107 | bool read(Register reg, size_t len, uint8_t* value); 108 | private: 109 | i2c_port_t _i2c; 110 | SemaphoreHandle_t _lock = nullptr; 111 | int8_t _age_offset = 0; 112 | }; 113 | 114 | #endif // _DS3231_H 115 | -------------------------------------------------------------------------------- /main/Display.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "Display.h" 26 | #include "freertos/task.h" 27 | #include "esp_log.h" 28 | 29 | #ifdef LV_LVGL_H_INCLUDE_SIMPLE 30 | #include "lvgl.h" 31 | #else 32 | #include "lvgl/lvgl.h" 33 | #endif 34 | #include "lvgl_helpers.h" 35 | 36 | #include "LVLabel.h" 37 | 38 | #ifndef DISPLAY_TASK_PRI 39 | #define DISPLAY_TASK_PRI 1 40 | #endif 41 | 42 | #ifndef DISPLAY_LV_TICK_MS 43 | #define DISPLAY_LV_TICK_MS 5 44 | #endif 45 | 46 | #ifndef DISPLAY_TASK_CORE 47 | #define DISPLAY_TASK_CORE 0 48 | #endif 49 | 50 | static const char* TAG = "Display"; 51 | 52 | Display& Display::getDisplay() 53 | { 54 | static Display* display; 55 | if (display == nullptr) 56 | { 57 | display = new Display(); 58 | } 59 | 60 | return *display; 61 | } 62 | 63 | Display::Display() 64 | { 65 | } 66 | 67 | bool Display::begin() 68 | { 69 | // there is only one Display object so this "should" be safe. 70 | _lock = xSemaphoreCreateRecursiveMutex(); 71 | 72 | ESP_LOGI(TAG, "calling: lv_init"); 73 | lv_init(); 74 | 75 | /* Initialize SPI or I2C bus used by the drivers */ 76 | ESP_LOGI(TAG, "calling: lvgl_driver_init"); 77 | lvgl_driver_init(); 78 | 79 | /* Use double buffered when not working with monochrome displays */ 80 | static lv_color_t buf1[DISP_BUF_SIZE]; 81 | #ifndef CONFIG_LV_TFT_DISPLAY_MONOCHROME 82 | static lv_color_t buf2[DISP_BUF_SIZE]; 83 | #endif 84 | 85 | static lv_disp_buf_t disp_buf; 86 | 87 | uint32_t size_in_px = DISP_BUF_SIZE; 88 | 89 | /* Initialize the working buffer depending on the selected display */ 90 | 91 | ESP_LOGI(TAG, "calling: lv_disp_buf_init"); 92 | #if defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_IL3820 \ 93 | || defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_JD79653A \ 94 | || defined CONFIG_LV_TFT_DISPLAY_CONTROLLER_UC8151D 95 | /* Actual size in pixel, not bytes and use single buffer */ 96 | size_in_px *= 8; 97 | lv_disp_buf_init(&disp_buf, buf1, NULL, size_in_px); 98 | #elif defined CONFIG_LV_TFT_DISPLAY_MONOCHROME 99 | lv_disp_buf_init(&disp_buf, buf1, NULL, size_in_px); 100 | #else 101 | lv_disp_buf_init(&disp_buf, buf1, buf2, size_in_px); 102 | #endif 103 | 104 | ESP_LOGI(TAG, "calling: lv_disp_drv_init"); 105 | lv_disp_drv_t disp_drv; 106 | lv_disp_drv_init(&disp_drv); 107 | disp_drv.flush_cb = disp_driver_flush; 108 | 109 | /* When using a monochrome display we need to register the callbacks: 110 | * - rounder_cb 111 | * - set_px_cb */ 112 | #ifdef CONFIG_LV_TFT_DISPLAY_MONOCHROME 113 | disp_drv.rounder_cb = disp_driver_rounder; 114 | disp_drv.set_px_cb = disp_driver_set_px; 115 | #endif 116 | 117 | ESP_LOGI(TAG, "calling: lv_disp_drv_register"); 118 | disp_drv.buffer = &disp_buf; 119 | _disp = lv_disp_drv_register(&disp_drv); 120 | 121 | /* Register an input device when enabled on the menuconfig */ 122 | #if CONFIG_LV_TOUCH_CONTROLLER != TOUCH_CONTROLLER_NONE 123 | ESP_LOGI(TAG, "calling: lv_indev_drv_init"); 124 | lv_indev_drv_t indev_drv; 125 | lv_indev_drv_init(&indev_drv); 126 | indev_drv.read_cb = touch_driver_read; 127 | indev_drv.type = LV_INDEV_TYPE_POINTER; 128 | ESP_LOGI(TAG, "calling: lv_indev_drv_register"); 129 | lv_indev_drv_register(&indev_drv); 130 | #endif 131 | // quite these 132 | esp_log_level_set("XPT2046", ESP_LOG_WARN); 133 | esp_log_level_set("spi_master", ESP_LOG_WARN); 134 | 135 | /* Create and start a periodic timer interrupt to call lv_tick_inc */ 136 | ESP_LOGI(TAG, "starting lv_tick_action timer"); 137 | const esp_timer_create_args_t periodic_timer_args = { 138 | .callback = &Display::tickAction, 139 | .arg = nullptr, 140 | .dispatch_method = ESP_TIMER_TASK, 141 | .name = "lv_tick_action" 142 | }; 143 | esp_timer_handle_t periodic_timer; 144 | ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer)); 145 | ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, DISPLAY_LV_TICK_MS*1000)); // (param is microseconds) 146 | 147 | ESP_LOGI(TAG, "Creating tabview"); 148 | _tabview = new LVTabView(nullptr); 149 | _tabview->setAnimationTime(0); 150 | 151 | ESP_LOGI(TAG, "Creating style for tabview"); 152 | static LVStyle style_tab_btn; 153 | style_tab_btn.setPad(LV_STATE_DEFAULT, LV_DPX(1), LV_DPX(1), LV_DPX(1), LV_DPX(1)); 154 | _tabview->addStyle(LV_TABVIEW_PART_TAB_BTN , &style_tab_btn); 155 | 156 | xTaskCreatePinnedToCore(task, "Display", 4096*2, NULL, DISPLAY_TASK_PRI, NULL, DISPLAY_TASK_CORE); 157 | return true; 158 | } 159 | 160 | Display::~Display() 161 | { 162 | } 163 | 164 | LVPage* Display::newPage(const char* name) 165 | { 166 | ESP_LOGI(TAG, "::newPage name='%s'", name); 167 | LVPage* page = nullptr; 168 | lock(-1000); 169 | page = _tabview->addTab(name); 170 | unlock(); 171 | return page; 172 | } 173 | 174 | void Display::task(void* data) 175 | { 176 | ESP_LOGI(TAG, "::task starting!"); 177 | while(true) 178 | { 179 | Display::getDisplay().lock(-1000); 180 | lv_task_handler(); /* let the GUI do its work */ 181 | Display::getDisplay().unlock(); 182 | vTaskDelay(pdMS_TO_TICKS(50)); 183 | } 184 | } 185 | 186 | void Display::tickAction(void *arg) { 187 | (void) arg; // unused 188 | lv_tick_inc(DISPLAY_LV_TICK_MS); 189 | } 190 | 191 | bool Display::lock(int timeout_ms) 192 | { 193 | if (!_lock) 194 | { 195 | ESP_LOGE(TAG, "::lock _lock not initialized!"); 196 | return false; 197 | } 198 | 199 | if (timeout_ms < 0) 200 | { 201 | while (xSemaphoreTakeRecursive( _lock, pdMS_TO_TICKS(-timeout_ms) ) != pdTRUE) 202 | { 203 | ESP_LOGE(TAG, "::lock failed to take lock within 1 second, retrying"); 204 | } 205 | return true; 206 | } 207 | 208 | return xSemaphoreTakeRecursive( _lock, pdMS_TO_TICKS(timeout_ms) ) == pdTRUE; 209 | } 210 | 211 | void Display::unlock() 212 | { 213 | xSemaphoreGiveRecursive(_lock); 214 | } 215 | 216 | uint32_t Display::getIdleTime() 217 | { 218 | if (_disp == nullptr) 219 | { 220 | return 0; 221 | } 222 | return lv_disp_get_inactive_time(_disp) / 1000; // return in seconds 223 | } 224 | -------------------------------------------------------------------------------- /main/Display.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _DISPLAY_H 26 | #define _DISPLAY_H 27 | 28 | #include "freertos/FreeRTOS.h" 29 | #include "freertos/semphr.h" 30 | #include "LVTabView.h" 31 | 32 | class Display 33 | { 34 | public: 35 | static Display& getDisplay(); 36 | ~Display(); 37 | bool begin(); 38 | LVPage* newPage(const char* name); 39 | bool lock(int timeout_ms); // negitive timeout is wait forever 40 | void unlock(); 41 | uint32_t getIdleTime(); 42 | private: 43 | Display(); 44 | SemaphoreHandle_t _lock = nullptr; 45 | LVTabView* _tabview = nullptr; 46 | lv_disp_t* _disp = nullptr; 47 | static void task(void* data); 48 | static void tickAction(void* data); 49 | }; 50 | #endif // _DISPLAY_H 51 | -------------------------------------------------------------------------------- /main/FieldText.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "FieldText.h" 26 | #include "esp_log.h" 27 | 28 | static const char* TAG = "FieldText"; 29 | 30 | LVStyle* FieldText::_container_style; 31 | LVStyle* FieldText::_textarea_style; 32 | LVKeyboard* FieldText::_keyboard; 33 | std::function FieldText::_kb_submit_action; 34 | std::function FieldText::_kb_cancel_action; 35 | 36 | FieldText::FieldText(LVBase* parent, const char* label, uint16_t length, const std::function submit, const std::function cancel) 37 | { 38 | if (_container_style == nullptr) 39 | { 40 | _container_style = new LVStyle(); 41 | _container_style->setPadInner(LV_STATE_DEFAULT, LV_DPX(2)); 42 | _container_style->setPad(LV_STATE_DEFAULT, LV_DPX(1), LV_DPX(1), LV_DPX(1), LV_DPX(1)); 43 | _container_style->setMargin(LV_STATE_DEFAULT, 0, 0, 0, 0); 44 | _container_style->setBorderWidth(LV_STATE_DEFAULT, 0); 45 | _container_style->setShadowWidth(LV_STATE_DEFAULT, 0); 46 | } 47 | 48 | if (_textarea_style == nullptr) 49 | { 50 | _textarea_style = new LVStyle(); 51 | _textarea_style->setPadInner(LV_STATE_DEFAULT, LV_DPX(2)); 52 | _textarea_style->setPad(LV_STATE_DEFAULT, LV_DPX(1), LV_DPX(1), LV_DPX(1), LV_DPX(1)); 53 | _textarea_style->setMargin(LV_STATE_DEFAULT, 0, 0, 0, 0); 54 | _textarea_style->setBorderWidth(LV_STATE_DEFAULT, 1); 55 | _textarea_style->setShadowWidth(LV_STATE_DEFAULT, 0); 56 | } 57 | 58 | if (_keyboard == nullptr) 59 | { 60 | _keyboard = new LVKeyboard(nullptr); 61 | _keyboard->setCurserManage(true); 62 | _keyboard->setHidden(true); 63 | _keyboard->setX(0); 64 | _keyboard->setEventCB([this](lv_event_t event) { 65 | _keyboard->defaultEventCB(event); 66 | //ESP_LOGI(TAG, "_keyboard event: %s", _keyboard->getEventName(event)); 67 | switch (event) 68 | { 69 | case LV_EVENT_APPLY: 70 | if (_kb_submit_action) 71 | { 72 | _kb_submit_action(); 73 | _kb_submit_action = nullptr; 74 | } 75 | _keyboard->setTextArea(nullptr); 76 | _keyboard->setHidden(true); 77 | break; 78 | 79 | case LV_EVENT_CANCEL: 80 | if (_kb_cancel_action) 81 | { 82 | _kb_cancel_action(); 83 | _kb_cancel_action = nullptr; 84 | } 85 | _keyboard->setTextArea(nullptr); 86 | _keyboard->setHidden(true); 87 | break; 88 | 89 | default: 90 | break; 91 | } 92 | }); 93 | } 94 | 95 | _cont = new LVContainer(parent); 96 | _cont->addStyle(LV_CONT_PART_MAIN, _container_style); 97 | _cont->setFit(LV_FIT_TIGHT); 98 | //_cont->setLayout(LV_LAYOUT_ROW_MID); 99 | _cont->setDragParent(true); 100 | 101 | _label = new LVLabel(_cont); 102 | _label->setStaticText(label); 103 | _label->setDragParent(true); 104 | _label->align(_cont, LV_ALIGN_IN_TOP_LEFT, 0, 0); 105 | 106 | _textarea = new LVTextArea(_cont); 107 | _textarea->addStyle(LV_TEXTAREA_PART_BG, _textarea_style); 108 | _textarea->setOneLine(true); 109 | _textarea->setMaxLength(length); 110 | _textarea->setPlaceholderText(""); 111 | _textarea->setText(""); 112 | _textarea->setCurserHidden(true); 113 | _textarea->align(_label, LV_ALIGN_OUT_RIGHT_MID, 0, 0); 114 | _textarea->setEventCB([this, submit, cancel](lv_event_t event){ 115 | switch (event) 116 | { 117 | case LV_EVENT_PRESSED: 118 | ESP_LOGI(TAG, "keyboard points to '%s'", _label->getText()); 119 | // setup the kb actions to cancle or save 120 | _kb_submit_action = submit; 121 | _kb_cancel_action = cancel; 122 | _keyboard->setMode(_mode); 123 | _keyboard->setTextArea(_textarea); 124 | _keyboard->setParent(_cont); 125 | _keyboard->alignY(_label, LV_ALIGN_OUT_BOTTOM_LEFT, 0); 126 | _keyboard->setHidden(false); 127 | break; 128 | default: 129 | break; 130 | } 131 | }); 132 | } 133 | 134 | void FieldText::setText(const char* text) 135 | { 136 | _textarea->setText(text); 137 | } 138 | 139 | const char* FieldText::getText() 140 | { 141 | return _textarea->getText(); 142 | } 143 | 144 | void FieldText::setPasswordMode(bool en) 145 | { 146 | _textarea->setPasswordMode(en); 147 | } 148 | 149 | void FieldText::setKeyboardMode(lv_keyboard_mode_t mode) 150 | { 151 | _mode = mode; 152 | } 153 | -------------------------------------------------------------------------------- /main/FieldText.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _FIELD_TEXT_H_ 26 | #define _FIELD_TEXT_H_ 27 | 28 | #include "LVStyle.h" 29 | #include "LVContainer.h" 30 | #include "LVLabel.h" 31 | #include "LVTextArea.h" 32 | #include "LVKeyboard.h" 33 | 34 | class FieldText { 35 | public: 36 | FieldText(LVBase* parent, const char* label, uint16_t length, std::function submit, std::function cancel); 37 | FieldText(FieldText&) = delete; 38 | FieldText& operator=(FieldText&) = delete; 39 | ~FieldText(); 40 | 41 | void setText(const char* text); 42 | const char* getText(); 43 | void setPasswordMode(bool en); 44 | void setKeyboardMode(lv_keyboard_mode_t mode); 45 | 46 | private: 47 | LVContainer* _cont; 48 | LVLabel* _label; 49 | LVTextArea* _textarea; 50 | lv_keyboard_mode_t _mode = LV_KEYBOARD_MODE_TEXT_LOWER; 51 | static LVStyle* _container_style; 52 | static LVStyle* _textarea_style; 53 | static LVKeyboard* _keyboard; 54 | static std::function _kb_submit_action; 55 | static std::function _kb_cancel_action; 56 | }; 57 | 58 | #endif // _FIELD_TEXT_H_ 59 | -------------------------------------------------------------------------------- /main/GPS.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _GPS_H 26 | #define _GPS_H 27 | #include "freertos/FreeRTOS.h" 28 | #include "freertos/queue.h" 29 | #include "freertos/task.h" 30 | #include "driver/uart.h" 31 | #include "driver/gpio.h" 32 | #include "driver/timer.h" 33 | #include "minmea.h" 34 | #include 35 | #include "PPS.h" 36 | #include "MicroSecondTimer.h" 37 | 38 | class GPS 39 | { 40 | 41 | public: 42 | explicit GPS(MicroSecondTimer& timer, uart_port_t uart_id = UART_NUM_1, size_t buffer_size = 2048); 43 | bool begin(gpio_num_t tx_pin, gpio_num_t rx_pin); 44 | 45 | // from GSV 46 | int getSatsTotal(); 47 | // from GGA 48 | int getSatsTracked(); 49 | int getFixQuality(); 50 | float getAltitude(); 51 | char getAltitudeUnits(); 52 | // from GSA 53 | char getMode(); 54 | int getFixType(); 55 | // from RMC 56 | bool getValid(uint32_t max_wait_ms=10); 57 | uint32_t getValidDuration(); // valid duration in seconds 58 | uint32_t getValidCount(); 59 | float getLatitude(); 60 | float getLongitude(); 61 | char* getPSTI(); 62 | time_t getRMCTime(); 63 | time_t getZDATime(); 64 | 65 | protected: 66 | MicroSecondTimer& _timer; 67 | uart_port_t _uart_id; 68 | size_t _buffer_size; 69 | char* _buffer = nullptr; 70 | // from GSV 71 | volatile int _sats_total = 0; 72 | //from GGA 73 | volatile int _sats_tracked = 0; 74 | volatile int _fix_quality = 0; 75 | volatile float _altitude = 0; 76 | volatile char _altitude_units = 0; 77 | // from GSA 78 | volatile char _mode = 0; 79 | volatile int _fix_type = 0; 80 | // from RMC 81 | volatile bool _valid = false; 82 | volatile float _latitude = 0.0; 83 | volatile float _longitude = 0.0; 84 | char _psti[81] = {0}; 85 | struct timespec _rmc_time = {0,0}; 86 | volatile uint64_t _last_rmc = 0; 87 | volatile uint64_t _valid_since = 0; 88 | volatile uint32_t _valid_count; 89 | // from ZDA if present 90 | struct timespec _zda_time = {0,0};; 91 | 92 | private: 93 | SemaphoreHandle_t _lock; 94 | QueueHandle_t _event_queue; 95 | TaskHandle_t _task; 96 | 97 | void process(char* sentence); 98 | void task(); 99 | static void task(void* data); 100 | 101 | }; 102 | 103 | #endif // _GPS_H 104 | -------------------------------------------------------------------------------- /main/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | menu "GPS NTP Server" 2 | 3 | config GPSNTP_PPS_PIN 4 | int "GPS PPS GPIO number" 5 | default 35 6 | help 7 | GPIO that has GPS PPS signal connected 8 | 9 | config GPSNTP_SQW_PIN 10 | int "RTC SQW GPIO number" 11 | default 26 12 | help 13 | GPIO that has RTC SQW signal connected 14 | 15 | config GPSNTP_RTC_DRIFT_MAX 16 | int "Maximum drift for RTC pulse" 17 | default 500 18 | help 19 | Maximum drift for RTC pulse 20 | 21 | choice GPSNTP_GPS_TYPE 22 | 23 | prompt "Select GPS type" 24 | default GPSNTP_GPS_TYPE_GENERIC 25 | help 26 | Select the GPS module type. 27 | 28 | config GPSNTP_GPS_TYPE_GENERIC 29 | bool "GPS type is GENERIC" 30 | 31 | config GPSNTP_GPS_TYPE_UBLOX6M 32 | bool "GPS type is UBLOX 6M" 33 | 34 | config GPSNTP_GPS_TYPE_SKYTRAQ 35 | bool "GPS type is SKYTRAQ" 36 | 37 | config GPSNTP_GPS_TYPE_MTK3339 38 | bool "GPS type is MTK3339 (Adafruit Untimate GPS)" 39 | 40 | endchoice 41 | 42 | config GPSNTP_ENABLE_115220BAUD 43 | depends on !GPSNTP_GPS_TYPE_GENERIC 44 | bool "Enable 115200 baud" 45 | default n 46 | 47 | endmenu 48 | -------------------------------------------------------------------------------- /main/MicroSecondTimer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "MicroSecondTimer.h" 26 | #include "esp_log.h" 27 | 28 | static const char* TAG = "TIMER"; 29 | 30 | MicroSecondTimer::MicroSecondTimer() 31 | { 32 | ESP_LOGI(TAG, "::MicroSecondTimer configuring and starting microsecond timer"); 33 | timer_config_t tc = { 34 | .alarm_en = TIMER_ALARM_DIS, 35 | .counter_en = TIMER_PAUSE, 36 | .intr_type = TIMER_INTR_LEVEL, 37 | .counter_dir = TIMER_COUNT_UP, 38 | .auto_reload = TIMER_AUTORELOAD_DIS, 39 | .divider = 80, // microseconds second 40 | }; 41 | esp_err_t err = timer_init(MICRO_SECOND_TIMER_GROUP_NUM, MICRO_SECOND_TIMER_NUM, &tc); 42 | if (err != ESP_OK) 43 | { 44 | ESP_LOGE(TAG, "timer_init failed: group=%d timer=%d err=%d (%s)", 45 | MICRO_SECOND_TIMER_GROUP_NUM, MICRO_SECOND_TIMER_NUM, err, esp_err_to_name(err)); 46 | } 47 | timer_set_counter_value(MICRO_SECOND_TIMER_GROUP_NUM, MICRO_SECOND_TIMER_NUM, 0x00000000ULL); 48 | timer_start(MICRO_SECOND_TIMER_GROUP_NUM, MICRO_SECOND_TIMER_NUM); 49 | } 50 | -------------------------------------------------------------------------------- /main/MicroSecondTimer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef __MICRO_SECOND_TIMER_H 26 | #define __MICRO_SECOND_TIMER_H 27 | 28 | #include "freertos/FreeRTOS.h" 29 | #include "driver/timer.h" 30 | #include 31 | #include 32 | 33 | #define MICRO_SECOND_TIMER_GROUP_NUM TIMER_GROUP_0 34 | #define MICRO_SECOND_TIMER_GROUP TIMERG0 35 | #define MICRO_SECOND_TIMER_NUM TIMER_0 36 | 37 | class MicroSecondTimer 38 | { 39 | public: 40 | MicroSecondTimer(); 41 | 42 | uint32_t inline IRAM_ATTR getValue() 43 | { 44 | TIMERG0.hw_timer[TIMER_0].update = 1; 45 | // Timer register is in a different clock domain from Timer hardware logic 46 | // We need to wait for the update to take effect before fetching the count value 47 | while (TIMERG0.hw_timer[TIMER_0].update) {} 48 | return TIMERG0.hw_timer[TIMER_0].cnt_low; 49 | } 50 | 51 | uint64_t inline IRAM_ATTR getValue64() 52 | { 53 | uint64_t value; 54 | timer_get_counter_value(MICRO_SECOND_TIMER_GROUP_NUM, MICRO_SECOND_TIMER_NUM, &value); 55 | return value; 56 | } 57 | }; 58 | 59 | #endif // __MICRO_SECOND_TIMER_H 60 | -------------------------------------------------------------------------------- /main/NTP.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "NTP.h" 26 | #include "Network.h" 27 | #include "esp_log.h" 28 | #include "math.h" 29 | 30 | #include "lwip/err.h" 31 | #include "lwip/sockets.h" 32 | #include "lwip/sys.h" 33 | #include 34 | 35 | static const char* TAG = "NTP"; 36 | 37 | //#define NTP_PACKET_DEBUG 38 | 39 | #ifndef NTP_TASK_PRI 40 | #define NTP_TASK_PRI configMAX_PRIORITIES-3 41 | #endif 42 | 43 | #ifndef NTP_TASK_CORE 44 | #define NTP_TASK_CORE 1 45 | #endif 46 | 47 | //#define NTP_PACKET_DEBUG 48 | 49 | #define NTP_PORT 123 50 | #define PRECISION_COUNT 10000 51 | 52 | typedef struct ntp_packet 53 | { 54 | uint8_t flags; 55 | uint8_t stratum; 56 | uint8_t poll; 57 | int8_t precision; 58 | uint32_t delay; 59 | uint32_t dispersion; 60 | uint8_t ref_id[4]; 61 | NTPTime ref_time; 62 | NTPTime orig_time; 63 | NTPTime recv_time; 64 | NTPTime xmit_time; 65 | } NTPPacket; 66 | 67 | #define LI_NONE 0 68 | #define LI_SIXTY_ONE 1 69 | #define LI_FIFTY_NINE 2 70 | #define LI_NOSYNC 3 71 | 72 | #define MODE_RESERVED 0 73 | #define MODE_ACTIVE 1 74 | #define MODE_PASSIVE 2 75 | #define MODE_CLIENT 3 76 | #define MODE_SERVER 4 77 | #define MODE_BROADCAST 5 78 | #define MODE_CONTROL 6 79 | #define MODE_PRIVATE 7 80 | 81 | #define NTP_VERSION 4 82 | 83 | #define REF_ID "PPS " // "GPS " when we have one! 84 | 85 | #define setLI(value) ((value&0x03)<<6) 86 | #define setVERS(value) ((value&0x07)<<3) 87 | #define setMODE(value) ((value&0x07)) 88 | 89 | #define getLI(value) ((value>>6)&0x03) 90 | #define getVERS(value) ((value>>3)&0x07) 91 | #define getMODE(value) (value&0x07) 92 | 93 | #define SEVENTY_YEARS 2208988800L 94 | #define toEPOCH(t) ((uint32_t)t-SEVENTY_YEARS) 95 | #define toNTP(t) ((uint32_t)t+SEVENTY_YEARS) 96 | 97 | #ifdef NTP_PACKET_DEBUG 98 | #include 99 | char* timestr(long int t) 100 | { 101 | t = toEPOCH(t); 102 | return ctime(&t); 103 | } 104 | 105 | void dumpNTPPacket(NTPPacket* ntp) 106 | { 107 | ESP_LOGI(TAG, "size: %u", sizeof(*ntp)); 108 | ESP_LOGI(TAG, "firstbyte: 0x%02x", *(uint8_t*)ntp); 109 | ESP_LOGI(TAG, "li: %u", getLI(ntp->flags)); 110 | ESP_LOGI(TAG, "version: %u", getVERS(ntp->flags)); 111 | ESP_LOGI(TAG, "mode: %u", getMODE(ntp->flags)); 112 | ESP_LOGI(TAG, "stratum: %u", ntp->stratum); 113 | ESP_LOGI(TAG, "poll: %u", ntp->poll); 114 | ESP_LOGI(TAG, "precision: %d", ntp->precision); 115 | ESP_LOGI(TAG, "delay: %u", ntp->delay); 116 | ESP_LOGI(TAG, "dispersion: %u", ntp->dispersion); 117 | ESP_LOGI(TAG, "ref_id: %02x:%02x:%02x:%02x", ntp->ref_id[0], ntp->ref_id[1], ntp->ref_id[2], ntp->ref_id[3]); 118 | ESP_LOGI(TAG, "ref_time: %08x:%08x", ntp->ref_time.seconds, ntp->ref_time.fraction); 119 | ESP_LOGI(TAG, "orig_time: %08x:%08x", ntp->orig_time.seconds, ntp->orig_time.fraction); 120 | ESP_LOGI(TAG, "recv_time: %08x:%08x", ntp->recv_time.seconds, ntp->recv_time.fraction); 121 | ESP_LOGI(TAG, "xmit_time: %08x:%08x", ntp->xmit_time.seconds, ntp->xmit_time.fraction); 122 | } 123 | #else 124 | #define dumpNTPPacket(x) 125 | #endif 126 | 127 | 128 | NTP::NTP(PPS& pps) 129 | : _pps(pps) 130 | { 131 | } 132 | 133 | NTP::~NTP() 134 | { 135 | } 136 | 137 | void NTP::begin() 138 | { 139 | _precision = computePrecision(); 140 | xTaskCreatePinnedToCore(&NTP::task, "NTP", 4096, this, NTP_TASK_PRI, nullptr, NTP_TASK_CORE); 141 | } 142 | 143 | int8_t NTP::computePrecision() 144 | { 145 | NTPTime t; 146 | uint64_t start = esp_timer_get_time(); 147 | for (int i = 0; i < PRECISION_COUNT; ++i) 148 | { 149 | getNTPTime(&t); 150 | } 151 | uint64_t end = esp_timer_get_time(); 152 | double total = (double)(end - start) / 1000000.0; 153 | double time = total / PRECISION_COUNT; 154 | double prec = log2(time); 155 | ESP_LOGI(TAG, "computePrecision: total:%f time:%f prec:%f (%d)", total, time, prec, (int8_t)prec); 156 | return (int8_t)prec; 157 | } 158 | 159 | #define us2s(x) (((double)x)/(double)1000000) // microseconds to seconds 160 | 161 | void NTP::getNTPTime(NTPTime* time) 162 | { 163 | struct timeval tv; 164 | _pps.getTime(&tv); 165 | time->seconds = toNTP(tv.tv_sec); 166 | double percent = us2s(tv.tv_usec); 167 | time->fraction = (uint32_t)(percent * (double)4294967296L); 168 | } 169 | 170 | 171 | void NTP::task(void* data) 172 | { 173 | NTP* ntp = static_cast(data); 174 | ESP_LOGI(TAG, "::task started with priority %d core %d", uxTaskPriorityGet(nullptr), xPortGetCoreID()); 175 | int addr_family = AF_INET; 176 | int ip_protocol = IPPROTO_IP; 177 | char addr_str[128]; 178 | NTPPacket packet; 179 | NTPTime recv_time; 180 | 181 | while(true) 182 | { 183 | struct sockaddr_in dest_addr; 184 | dest_addr.sin_addr.s_addr = htonl(INADDR_ANY); 185 | dest_addr.sin_family = AF_INET; 186 | dest_addr.sin_port = htons(NTP_PORT); 187 | inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1); 188 | 189 | // wait till there is a network connected 190 | Network::getNetwork().waitFor(Network::HAS_IP); 191 | 192 | int sock = socket(addr_family, SOCK_DGRAM, ip_protocol); 193 | if (sock < 0) { 194 | ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); 195 | break; 196 | } 197 | ESP_LOGI(TAG, "Socket created"); 198 | 199 | int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); 200 | if (err < 0) { 201 | ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno); 202 | break; 203 | } 204 | ESP_LOGI(TAG, "Socket bound, port %d", NTP_PORT); 205 | 206 | while(true) 207 | { 208 | ESP_LOGD(TAG, "Waiting for data"); 209 | struct sockaddr_in6 source_addr; // Large enough for both IPv4 or IPv6 210 | socklen_t socklen = sizeof(source_addr); 211 | int len = recvfrom(sock, &packet, sizeof(packet), 0, (struct sockaddr *)&source_addr, &socklen); 212 | ntp->getNTPTime(&recv_time); 213 | ntp->_req_count++; 214 | 215 | // Error occurred during receiving 216 | if (len < 0) { 217 | ESP_LOGE(TAG, "recvfrom failed: errno %d", errno); 218 | break; 219 | } 220 | if (len != sizeof(packet)) 221 | { 222 | ESP_LOGE(TAG, "bad packet size: %u != %u", len, sizeof(packet)); 223 | continue; 224 | } 225 | 226 | // Get the sender's ip address as string 227 | if (source_addr.sin6_family == PF_INET) 228 | { 229 | inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1); 230 | } 231 | else if (source_addr.sin6_family == PF_INET6) 232 | { 233 | inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1); 234 | } 235 | ESP_LOGD(TAG, "Received %d bytes from %s:", len, addr_str); 236 | 237 | packet.delay = ntohl(packet.delay); 238 | packet.dispersion = ntohl(packet.dispersion); 239 | packet.orig_time.seconds = ntohl(packet.orig_time.seconds); 240 | packet.orig_time.fraction = ntohl(packet.orig_time.fraction); 241 | packet.ref_time.seconds = ntohl(packet.ref_time.seconds); 242 | packet.ref_time.fraction = ntohl(packet.ref_time.fraction); 243 | packet.recv_time.seconds = ntohl(packet.recv_time.seconds); 244 | packet.recv_time.fraction = ntohl(packet.recv_time.fraction); 245 | packet.xmit_time.seconds = ntohl(packet.xmit_time.seconds); 246 | packet.xmit_time.fraction = ntohl(packet.xmit_time.fraction); 247 | dumpNTPPacket(&packet); 248 | // 249 | // Build the response 250 | // 251 | packet.flags = setLI(LI_NONE) | setVERS(NTP_VERSION) | setMODE(MODE_SERVER); 252 | packet.stratum = 1; 253 | packet.precision = ntp->_precision; 254 | // TODO: compute actual root delay, and root dispersion 255 | packet.delay = 1; //(uint32)(0.000001 * 65536.0); 256 | packet.dispersion = 1; //(uint32_t)(_gps.getDispersion() * 65536.0); // TODO: pre-calculate this? 257 | memcpy((char*)packet.ref_id, REF_ID, sizeof(packet.ref_id)); 258 | packet.orig_time = packet.xmit_time; 259 | packet.recv_time = recv_time; 260 | ntp->getNTPTime(&(packet.ref_time)); 261 | dumpNTPPacket(&packet); 262 | packet.delay = htonl(packet.delay); 263 | packet.dispersion = htonl(packet.dispersion); 264 | packet.orig_time.seconds = htonl(packet.orig_time.seconds); 265 | packet.orig_time.fraction = htonl(packet.orig_time.fraction); 266 | packet.ref_time.seconds = htonl(packet.ref_time.seconds); 267 | packet.ref_time.fraction = htonl(packet.ref_time.fraction); 268 | packet.recv_time.seconds = htonl(packet.recv_time.seconds); 269 | packet.recv_time.fraction = htonl(packet.recv_time.fraction); 270 | ntp->getNTPTime(&(packet.xmit_time)); 271 | packet.xmit_time.seconds = htonl(packet.xmit_time.seconds); 272 | packet.xmit_time.fraction = htonl(packet.xmit_time.fraction); 273 | 274 | err = sendto(sock, &packet, sizeof(packet), 0, (struct sockaddr *)&source_addr, sizeof(source_addr)); 275 | if (err < 0) 276 | { 277 | ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); 278 | break; 279 | } 280 | ntp->_rsp_count++; 281 | } 282 | 283 | ESP_LOGE(TAG, "Shutting down socket and restarting..."); 284 | shutdown(sock, 0); 285 | close(sock); 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /main/NTP.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _NTP_H 26 | #define _NTP_H 27 | #include "PPS.h" 28 | 29 | typedef struct ntp_time 30 | { 31 | uint32_t seconds; 32 | uint32_t fraction; 33 | } NTPTime; 34 | 35 | class NTP 36 | { 37 | public: 38 | explicit NTP(PPS& pps); 39 | ~NTP(); 40 | void begin(); 41 | uint32_t getRequests() { return _req_count; } 42 | uint32_t getResponses() { return _rsp_count; } 43 | 44 | private: 45 | PPS& _pps; 46 | uint32_t _req_count; 47 | uint32_t _rsp_count; 48 | uint8_t _precision; 49 | 50 | void getNTPTime(NTPTime *time); 51 | int8_t computePrecision(); 52 | void handleRequest(); 53 | static void task(void* data); 54 | }; 55 | 56 | #endif // _NTP_H 57 | -------------------------------------------------------------------------------- /main/Network.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "Network.h" 26 | #include "freertos/task.h" 27 | #include "freertos/event_groups.h" 28 | #include "esp_system.h" 29 | #include "esp_wifi.h" 30 | #include "esp_log.h" 31 | #include "memory.h" 32 | 33 | static const char* TAG = "Network"; 34 | static EventGroupHandle_t network_status; 35 | 36 | Network::Network() 37 | { 38 | } 39 | 40 | Network& Network::getNetwork() 41 | { 42 | static Network* network; 43 | if (network == nullptr) 44 | { 45 | network = new Network(); 46 | } 47 | return *network; 48 | } 49 | 50 | bool Network::begin(const char* ssid, const char* password) 51 | { 52 | ESP_LOGI(TAG, "::begin starting"); 53 | network_status = xEventGroupCreate(); 54 | 55 | ESP_ERROR_CHECK(esp_netif_init()); 56 | 57 | ESP_ERROR_CHECK(esp_event_loop_create_default()); 58 | esp_netif_create_default_wifi_sta(); 59 | 60 | wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); 61 | ESP_ERROR_CHECK(esp_wifi_init(&cfg)); 62 | 63 | ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &Network::eventHandler, this)); 64 | ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &Network::eventHandler, this)); 65 | 66 | wifi_config_t wifi_config; 67 | memset(&wifi_config, 0, sizeof(wifi_config)); 68 | for(uint8_t i = 0; ssid[i] != 0; ++i) 69 | { 70 | wifi_config.sta.ssid[i] = ssid[i]; 71 | } 72 | for(uint8_t i = 0; password[i] != 0; ++i) 73 | { 74 | wifi_config.sta.password[i] = password[i]; 75 | } 76 | wifi_config.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; 77 | wifi_config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK; 78 | wifi_config.sta.pmf_cfg.capable = true; 79 | wifi_config.sta.pmf_cfg.required = false; 80 | 81 | ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); 82 | ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE)); // disable power save as it adds up to 300ms or more in latemcy! 83 | ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); 84 | ESP_ERROR_CHECK(esp_wifi_start() ); 85 | 86 | ESP_LOGI(TAG, "::begin finished"); 87 | 88 | return true; 89 | } 90 | 91 | bool Network::hasIP() 92 | { 93 | EventBits_t bits = xEventGroupWaitBits(network_status, HAS_IP, pdFALSE, pdFALSE, 0); 94 | return (bits & HAS_IP) == HAS_IP; 95 | } 96 | 97 | esp_ip4_addr_t Network::getIPAddress(char* buf, size_t size) 98 | { 99 | if (buf != nullptr && size != 0) 100 | { 101 | strncpy(buf, _ip_str, size); 102 | } 103 | return _ip; 104 | } 105 | 106 | uint32_t Network::waitFor(Status status, TickType_t wait) 107 | { 108 | return xEventGroupWaitBits(network_status, status, pdFALSE, pdFALSE, wait); 109 | } 110 | 111 | void Network::eventHandler(void* data, esp_event_base_t event_base, int32_t event_id, void* event_data) 112 | { 113 | Network* net = static_cast(data); 114 | 115 | if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) 116 | { 117 | xEventGroupClearBits(network_status, HAS_IP); 118 | memset(&net->_ip, 0, sizeof(net->_ip)); 119 | net->_ip_str[0] = '\0'; 120 | esp_wifi_connect(); 121 | ESP_LOGI(TAG, "station was started, initiated connect to AP"); 122 | } 123 | else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) 124 | { 125 | xEventGroupClearBits(network_status, HAS_IP); 126 | memset(&net->_ip, 0, sizeof(net->_ip)); 127 | net->_ip_str[0] = '\0'; 128 | esp_wifi_connect(); 129 | ESP_LOGI(TAG, "disconnected! retry to connect to the AP"); 130 | } 131 | else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) 132 | { 133 | ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; 134 | net->_ip = event->ip_info.ip; 135 | snprintf(net->_ip_str, sizeof(net->_ip_str), IPSTR, IP2STR(&event->ip_info.ip)); 136 | xEventGroupSetBits(network_status, HAS_IP); 137 | ESP_LOGI(TAG, "got ip: %s", net->_ip_str); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /main/Network.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _NETWORK_H 26 | #define _NETWORK_H 27 | #include "freertos/FreeRTOS.h" 28 | #include "esp_event.h" 29 | 30 | class Network 31 | { 32 | public: 33 | using Status = uint32_t; 34 | static const Status HAS_IP = BIT0; 35 | 36 | static Network& getNetwork(); 37 | ~Network(); 38 | bool begin(const char* ssid, const char* password); 39 | bool hasIP(); 40 | esp_ip4_addr_t getIPAddress(char* buf = nullptr, size_t size = 0); 41 | uint32_t waitFor(Status status, TickType_t wait = portMAX_DELAY); 42 | 43 | private: 44 | Network(); 45 | esp_ip4_addr_t _ip = {0}; 46 | char _ip_str[16] = {0}; 47 | static void eventHandler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data); 48 | }; 49 | 50 | #endif // _NETWORK_H 51 | -------------------------------------------------------------------------------- /main/PPS.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "PPS.h" 26 | //#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG 27 | #include "esp_log.h" 28 | #include 29 | #include 30 | #include "soc/soc.h" 31 | #include "driver/timer.h" 32 | 33 | static const char* TAG = "PPS"; 34 | 35 | PPS::PPS(MicroSecondTimer& timer, pps_data_t* data, PPS* ref) 36 | : _timer(timer), 37 | _data(data), 38 | _ref(ref) 39 | { 40 | memset(data, 0, sizeof(pps_data_t)); 41 | ESP_LOGI(TAG, "pps data %u bytes @ 0x%08x", sizeof(pps_data_t), (uint32_t)data); 42 | if (ref != nullptr) 43 | { 44 | data->pps_ref = ref->_data; 45 | } 46 | } 47 | 48 | bool PPS::begin(gpio_num_t pps_pin, bool expect_negedge) 49 | { 50 | _pin = pps_pin; 51 | _data->pps_pin = pps_pin; 52 | 53 | if (_pin != GPIO_NUM_NC) 54 | { 55 | ESP_LOGI(TAG, "::begin configuring PPS pin %d", _pin); 56 | gpio_set_direction(_pin, GPIO_MODE_INPUT); 57 | 58 | ESP_LOGI(TAG, "::begin setup ppsISR for highint5"); 59 | ESP_INTR_DISABLE(31); 60 | intr_matrix_set(1, ETS_GPIO_INTR_SOURCE, 31); 61 | ESP_INTR_ENABLE(31); 62 | gpio_set_intr_type(_pin, expect_negedge ? GPIO_INTR_NEGEDGE : GPIO_INTR_POSEDGE); 63 | gpio_intr_enable(_pin); 64 | } 65 | 66 | return true; 67 | } 68 | 69 | int PPS::getLevel() 70 | { 71 | return gpio_get_level(_pin); 72 | } 73 | 74 | /** 75 | * get current time & microseconds. 76 | */ 77 | time_t PPS::getTime(struct timeval* tv) 78 | { 79 | if (tv == nullptr) 80 | { 81 | return _data->pps_time; 82 | } 83 | 84 | // edge case! both seconds and _last_timer only change once a second, however, 85 | // it changes via an interrupt so we make sure we have good values by making sure 86 | // it is the same on second look 87 | do 88 | { 89 | tv->tv_sec = _data->pps_time; 90 | tv->tv_usec = _timer.getValue() - _data->pps_last; 91 | // timer could be slightly off, insure microseconds is not returned as (or more) than a full second! 92 | if (tv->tv_usec > 999999) 93 | { 94 | tv->tv_usec = 999999; 95 | } 96 | } while (tv->tv_sec != _data->pps_time); // insure we stay on the same seconds (to go with the microseconds) 97 | return tv->tv_sec; 98 | } 99 | 100 | /** 101 | * set the time, seconds only 102 | */ 103 | void PPS::setTime(time_t time) 104 | { 105 | _data->pps_time = time; 106 | } 107 | 108 | /** 109 | * get the minimum time in microseconds between PPS pulses 110 | */ 111 | uint32_t PPS::getTimerMin() 112 | { 113 | return _data->pps_min; 114 | } 115 | 116 | /** 117 | * get the last timer value 118 | */ 119 | uint32_t PPS::getTimerLast() 120 | { 121 | return _data->pps_last; 122 | } 123 | 124 | /** 125 | * get the maximum time in microseconds between PPS pulses 126 | */ 127 | uint32_t PPS::getTimerMax() 128 | { 129 | return _data->pps_max; 130 | } 131 | 132 | /** 133 | * get the interval in microseconds between PPS pulses 134 | */ 135 | uint32_t PPS::getTimerInterval() 136 | { 137 | return _data->pps_interval; 138 | } 139 | 140 | /** 141 | * get the number of PPS pulses considered short (and invalid) 142 | */ 143 | uint32_t PPS::getTimerShort() 144 | { 145 | return _data->pps_short; 146 | } 147 | 148 | /** 149 | * get the number of PPS pulses considered long (and invalid) 150 | */ 151 | uint32_t PPS::getTimerLong() 152 | { 153 | return _data->pps_long; 154 | } 155 | 156 | /** 157 | * get the offset from ref 158 | */ 159 | int32_t PPS::getOffset() 160 | { 161 | return _data->pps_offset; 162 | } 163 | 164 | /** 165 | * reset the offset from ref 166 | */ 167 | void PPS::resetOffset() 168 | { 169 | _data->pps_offset = 0; 170 | } 171 | 172 | /** 173 | * set disabled preventing the ISR from counting 174 | */ 175 | void PPS::setDisable(bool disable) 176 | { 177 | _data->pps_disabled = disable; 178 | } 179 | -------------------------------------------------------------------------------- /main/PPS.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _PPS_H 26 | #define _PPS_H 27 | #include "freertos/FreeRTOS.h" 28 | #include "freertos/queue.h" 29 | #include "freertos/task.h" 30 | #include "driver/uart.h" 31 | #include "driver/gpio.h" 32 | #include "driver/timer.h" 33 | #include "MicroSecondTimer.h" 34 | 35 | typedef struct pps_data 36 | { 37 | volatile uint32_t pps_pin; 38 | volatile uint32_t pps_last; 39 | volatile uint32_t pps_time; 40 | volatile uint32_t pps_min; 41 | volatile uint32_t pps_max; 42 | volatile uint32_t pps_interval; 43 | volatile int32_t pps_offset; 44 | volatile struct pps_data* pps_ref; 45 | volatile uint32_t pps_short; 46 | volatile uint32_t pps_long; 47 | volatile uint32_t pps_disabled; 48 | } pps_data_t; 49 | 50 | class PPS 51 | { 52 | public: 53 | PPS(MicroSecondTimer& timer, pps_data_t *data, PPS* ref = nullptr); 54 | bool begin(gpio_num_t pps_pin = GPIO_NUM_NC, bool expect_negedge = false); 55 | int getLevel(); 56 | time_t getTime(struct timeval* tv); 57 | void setTime(time_t time); 58 | uint32_t getTimerLast(); 59 | uint32_t getTimerMin(); 60 | uint32_t getTimerMax(); 61 | uint32_t getTimerInterval(); 62 | uint32_t getTimerShort(); 63 | uint32_t getTimerLong(); 64 | int32_t getOffset(); 65 | void resetOffset(); 66 | void setDisable(bool disable); 67 | 68 | protected: 69 | MicroSecondTimer& _timer; 70 | pps_data_t* _data; 71 | PPS* _ref; 72 | gpio_num_t _pin = GPIO_NUM_NC; 73 | private: 74 | static void pps(void* data); 75 | 76 | }; 77 | 78 | #endif // _PPS_H 79 | -------------------------------------------------------------------------------- /main/PageAbout.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "PageAbout.h" 26 | #include "Display.h" 27 | #include "WithDisplayLock.h" 28 | #include "LVContainer.h" 29 | #include "Network.h" 30 | 31 | #include "freertos/FreeRTOS.h" 32 | #include "freertos/task.h" 33 | #include "esp_log.h" 34 | 35 | static const char* TAG = "PageAbout"; 36 | 37 | PageAbout::PageAbout() 38 | { 39 | WithDisplayLock([this](){ 40 | _container_style.setPadInner(LV_STATE_DEFAULT, LV_DPX(2)); 41 | _container_style.setPad(LV_STATE_DEFAULT, LV_DPX(1), LV_DPX(1), LV_DPX(1), LV_DPX(1)); 42 | _container_style.setMargin(LV_STATE_DEFAULT, 0, 0, 0, 0); 43 | _container_style.setBorderWidth(LV_STATE_DEFAULT, 0); 44 | _container_style.setShadowWidth(LV_STATE_DEFAULT, 0); 45 | 46 | _page = Display::getDisplay().newPage("About"); 47 | _page->addStyle(LV_PAGE_PART_SCROLLABLE, &_container_style); 48 | 49 | LVContainer* cont = new LVContainer(_page); 50 | cont->setFit(LV_FIT_PARENT/*, LV_FIT_TIGHT*/); 51 | cont->addStyle(LV_CONT_PART_MAIN, &_container_style); 52 | cont->setLayout(LV_LAYOUT_COLUMN_LEFT); 53 | cont->align(nullptr, LV_ALIGN_CENTER, 0, 0); 54 | cont->setDragParent(true); 55 | 56 | _free = new LVLabel(cont); 57 | _address = new LVLabel(cont); 58 | _uptime = new LVLabel(cont); 59 | 60 | ESP_LOGI(TAG, "creating About task"); 61 | lv_task_create(task, 1000, LV_TASK_PRIO_LOW, this); 62 | }); 63 | } 64 | 65 | PageAbout::~PageAbout() 66 | { 67 | } 68 | 69 | void PageAbout::task(lv_task_t *task) 70 | { 71 | PageAbout* p = static_cast(task->user_data); 72 | p->update(); 73 | } 74 | 75 | void PageAbout::update() 76 | { 77 | char buf[64]; 78 | snprintf(buf, sizeof(buf)-1, "Free: %u", esp_get_free_heap_size()); 79 | _free->setText(buf); 80 | char addr[24]; 81 | Network::getNetwork().getIPAddress(addr, sizeof(addr)); 82 | snprintf(buf, sizeof(buf)-1, "Address: %s", addr); 83 | _address->setText(buf); 84 | 85 | uint32_t seconds = esp_timer_get_time() / 1000000; // uptime in seconds 86 | uint32_t days = seconds / 86400; 87 | seconds -= days * 86400; 88 | uint32_t hours = seconds /3600; 89 | seconds -= hours * 3600; 90 | uint32_t minutes = seconds / 60; 91 | seconds -= minutes * 60; 92 | snprintf(buf, sizeof(buf)-1, "Uptime: %ud %02uh %02um %02us", days, hours, minutes, seconds); 93 | _uptime->setText(buf); 94 | } 95 | -------------------------------------------------------------------------------- /main/PageAbout.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _PAGE_ABOUT_H_ 26 | #define _PAGE_ABOUT_H_ 27 | #include "LVPage.h" 28 | #include "LVLabel.h" 29 | 30 | class PageAbout { 31 | public: 32 | PageAbout(); 33 | ~PageAbout(); 34 | PageAbout(PageAbout&) = delete; 35 | PageAbout& operator=(PageAbout&) = delete; 36 | private: 37 | void update(); 38 | static void task(lv_task_t* task); 39 | LVPage* _page; 40 | LVLabel* _free; 41 | LVLabel* _address; 42 | LVLabel* _uptime; 43 | LVStyle _container_style; 44 | }; 45 | 46 | #endif // _PAGE_ABOUT_H_ 47 | -------------------------------------------------------------------------------- /main/PageConfig.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "PageConfig.h" 26 | #include "Display.h" 27 | #include "WithDisplayLock.h" 28 | #include "LVContainer.h" 29 | #include "LVLabel.h" 30 | #include "esp_log.h" 31 | 32 | static const char* TAG = "PageConfig"; 33 | 34 | static void configTask(lv_task_t* task) 35 | { 36 | PageConfig* p = static_cast(task->user_data); 37 | p->process(); 38 | } 39 | 40 | PageConfig::PageConfig(Config& config, const ApplyCB& apply_cb) 41 | : _config(config), 42 | _apply_cb(apply_cb) 43 | { 44 | WithDisplayLock lock; // this creates a lock that unlocks when destroyed 45 | 46 | _page = Display::getDisplay().newPage("Cfg"); 47 | _contrainer_style.setPadInner(LV_STATE_DEFAULT, LV_DPX(2)); 48 | _contrainer_style.setPad(LV_STATE_DEFAULT, LV_DPX(1), LV_DPX(1), LV_DPX(1), LV_DPX(1)); 49 | _contrainer_style.setMargin(LV_STATE_DEFAULT, 0, 0, 0, 0); 50 | _contrainer_style.setBorderWidth(LV_STATE_DEFAULT, 0); 51 | _contrainer_style.setShadowWidth(LV_STATE_DEFAULT, 0); 52 | 53 | _button_style.setPad(LV_STATE_DEFAULT, LV_DPX(4), LV_DPX(4), LV_DPX(4), LV_DPX(4)); 54 | _button_style.setMargin(LV_STATE_DEFAULT, 0, 0, 0, 0); 55 | 56 | _page->addStyle(LV_PAGE_PART_SCROLLABLE, &_contrainer_style); 57 | 58 | LVContainer* cont = new LVContainer(_page); 59 | cont->setFit(LV_FIT_PARENT, LV_FIT_TIGHT); 60 | cont->addStyle(LV_CONT_PART_MAIN, &_contrainer_style); 61 | cont->setLayout(LV_LAYOUT_COLUMN_MID); 62 | cont->align(nullptr, LV_ALIGN_IN_TOP_MID, 0, 0); 63 | cont->setDragParent(true); 64 | 65 | LVContainer* fcont = new LVContainer(cont); 66 | fcont->setFit(LV_FIT_TIGHT); 67 | fcont->addStyle(LV_CONT_PART_MAIN, &_contrainer_style); 68 | fcont->setLayout(LV_LAYOUT_COLUMN_LEFT); 69 | fcont->align(nullptr, LV_ALIGN_CENTER, 0, 0); 70 | fcont->setDragParent(true); 71 | 72 | _ssid = new FieldText(fcont, "SSID:", 32, 73 | [this](){ 74 | _config.setWiFiSSID(_ssid->getText()); 75 | }, 76 | [this](){ 77 | _ssid->setText(_config.getWiFiSSID()); 78 | }); 79 | _ssid->setText(_config.getWiFiSSID()); 80 | 81 | _password = new FieldText(fcont, "PSK:", 32, 82 | [this](){ 83 | _config.setWiFiPassword(_password->getText()); 84 | }, 85 | [this](){ 86 | _password->setText(_config.getWiFiPassword()); 87 | }); 88 | _password->setPasswordMode(true); 89 | _password->setText(_config.getWiFiPassword()); 90 | 91 | _bias = new FieldText(fcont, "Bias:", 32, 92 | [this](){ 93 | _config.setBias(atof(_bias->getText())); 94 | }, 95 | [this](){ 96 | char buf[32]; 97 | snprintf(buf, sizeof(buf)-1, "%0f", _config.getBias()); 98 | _bias->setText(buf); 99 | }); 100 | char buf[32]; 101 | snprintf(buf, sizeof(buf)-1, "%0f", _config.getBias()); 102 | _bias->setText(buf); 103 | _bias->setKeyboardMode(LV_KEYBOARD_MODE_NUM); 104 | 105 | _target = new FieldText(fcont, "Target:", 32, 106 | [this](){ 107 | _config.setTarget(atof(_target->getText())); 108 | }, 109 | [this](){ 110 | char buf[32]; 111 | snprintf(buf, sizeof(buf)-1, "%0f", _config.getTarget()); 112 | _target->setText(buf); 113 | }); 114 | snprintf(buf, sizeof(buf)-1, "%0f", _config.getTarget()); 115 | _target->setText(buf); 116 | _target->setKeyboardMode(LV_KEYBOARD_MODE_NUM); 117 | 118 | LVContainer* ctrls = new LVContainer(cont); 119 | ctrls->setFit(LV_FIT_TIGHT); 120 | ctrls->setLayout(LV_LAYOUT_ROW_MID); 121 | ctrls->align(nullptr, LV_ALIGN_CENTER, 0, 0); 122 | ctrls->addStyle(LV_CONT_PART_MAIN, &_contrainer_style); 123 | ctrls->setStylePadInner(LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 50); 124 | ctrls->setStyleMarginTop(LV_CONT_PART_MAIN, LV_STATE_DEFAULT, 10); 125 | ctrls->setDragParent(true); 126 | 127 | _load = makeButton(ctrls, "LOAD", [this](lv_event_t event){ 128 | if(event == LV_EVENT_CLICKED) { 129 | ESP_LOGI(TAG, "_load CB: loading"); 130 | if (!_config.load()) 131 | { 132 | ESP_LOGE(TAG, "_load CB: loading failed!"); 133 | } 134 | _ssid->setText(_config.getWiFiSSID()); 135 | _password->setText(_config.getWiFiPassword()); 136 | char buf[32]; 137 | snprintf(buf, sizeof(buf)-1, "%0f", _config.getBias()); 138 | _bias->setText(buf); 139 | snprintf(buf, sizeof(buf)-1, "%0f", _config.getTarget()); 140 | _target->setText(buf); 141 | } 142 | }); 143 | 144 | _save = makeButton(ctrls, "SAVE", [this](lv_event_t event){ 145 | if(event == LV_EVENT_CLICKED) { 146 | ESP_LOGI(TAG, "_save CB: saving!"); 147 | _config.save(); 148 | } 149 | }); 150 | 151 | _apply = makeButton(ctrls, "APPLY", [this](lv_event_t event){ 152 | if(event == LV_EVENT_CLICKED) { 153 | ESP_LOGI(TAG, "_apply CB: invoke callback!!"); 154 | if (_apply_cb) 155 | { 156 | _apply_cb(); 157 | } 158 | } 159 | }); 160 | 161 | ESP_LOGI(TAG, "init: creating lv task"); 162 | lv_task_create(configTask, 100, LV_TASK_PRIO_LOW, this); 163 | } 164 | 165 | PageConfig::~PageConfig() 166 | { 167 | } 168 | 169 | LVButton* PageConfig::makeButton(LVBase* parent, const char* label, LVBase::LVEventCB cb) 170 | { 171 | LVButton* btn = new LVButton(parent); 172 | btn->addStyle(LV_BTN_PART_MAIN, &_button_style); 173 | btn->setFit(LV_FIT_TIGHT, LV_FIT_TIGHT); 174 | btn->setEventCB(cb); 175 | LVLabel* lbl = new LVLabel(btn); 176 | lbl->setText(label); 177 | return btn; 178 | } 179 | 180 | LVSpinBox* PageConfig::makeSpinBoxField(LVBase* parent, const char* label, int32_t initial_value, LVBase::LVEventCB cb) 181 | { 182 | LVContainer* cont = new LVContainer(parent); 183 | cont->addStyle(LV_CONT_PART_MAIN, &_contrainer_style); 184 | cont->setFit(LV_FIT_TIGHT); 185 | cont->setLayout(LV_LAYOUT_ROW_MID); 186 | cont->setDragParent(true); 187 | 188 | LVLabel* l = new LVLabel(cont); 189 | l->setStaticText(label); 190 | l->setDragParent(true); 191 | 192 | LVButton* inc = new LVButton(cont); 193 | inc->applyTheme(LV_THEME_SPINBOX_BTN); 194 | inc->setStyleValueStr(LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_SYMBOL_PLUS); 195 | 196 | LVSpinBox* sb = new LVSpinBox(cont); 197 | sb->setEventCB(cb); 198 | ESP_LOGI(TAG, "makeSpinBoxField - initial value %d", initial_value); 199 | sb->setValue(initial_value); 200 | 201 | LVButton* dec = new LVButton(cont); 202 | dec->applyTheme(LV_THEME_SPINBOX_BTN); 203 | dec->setStyleValueStr(LV_BTN_PART_MAIN, LV_STATE_DEFAULT, LV_SYMBOL_MINUS); 204 | 205 | lv_coord_t h = sb->getHeight(); 206 | inc->setSize(h, h); 207 | dec->setSize(h, h); 208 | 209 | inc->setEventCB([this,sb](lv_event_t event){ 210 | if (event == LV_EVENT_SHORT_CLICKED || event == LV_EVENT_LONG_PRESSED_REPEAT) 211 | { 212 | sb->increment(); 213 | } 214 | }); 215 | dec->setEventCB([this,sb](lv_event_t event){ 216 | if (event == LV_EVENT_SHORT_CLICKED || event == LV_EVENT_LONG_PRESSED_REPEAT) 217 | { 218 | sb->decrement(); 219 | } 220 | }); 221 | 222 | return sb; 223 | } 224 | 225 | void PageConfig::process() 226 | { 227 | } 228 | -------------------------------------------------------------------------------- /main/PageConfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _PAGE_CONFIG_H 26 | #define _PAGE_CONFIG_H 27 | 28 | #include "Config.h" 29 | #include "LVPage.h" 30 | #include "LVButton.h" 31 | #include "LVContainer.h" 32 | #include "LVSpinBox.h" 33 | #include "LVTextArea.h" 34 | #include "LVKeyboard.h" 35 | #include "FieldText.h" 36 | 37 | class PageConfig 38 | { 39 | public: 40 | using ApplyCB = std::function; 41 | explicit PageConfig(Config& config, const ApplyCB& apply_cb = nullptr); 42 | ~PageConfig(); 43 | void process(); 44 | 45 | PageConfig(PageConfig&) = delete; 46 | PageConfig& operator=(PageConfig&) = delete; 47 | 48 | private: 49 | Config& _config; 50 | const ApplyCB& _apply_cb; 51 | 52 | LVStyle _contrainer_style; 53 | LVStyle _button_style; 54 | 55 | LVPage* _page; 56 | FieldText* _ssid; 57 | FieldText* _password; 58 | FieldText* _bias; 59 | FieldText* _target; 60 | LVButton* _apply; 61 | LVButton* _save; 62 | LVButton* _load; 63 | 64 | LVButton* makeButton(LVBase* parent, const char* label, LVBase::LVEventCB cb); 65 | LVSpinBox* makeSpinBoxField(LVBase* parent, const char* label, int32_t initial_value, LVBase::LVEventCB cb); 66 | }; 67 | #endif // _PAGE_CONFIG_H 68 | -------------------------------------------------------------------------------- /main/PageDelta.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "PageDelta.h" 26 | #include "Display.h" 27 | #include "WithDisplayLock.h" 28 | #include "LVContainer.h" 29 | 30 | #include "freertos/FreeRTOS.h" 31 | #include "freertos/task.h" 32 | #include "esp_log.h" 33 | 34 | static const char* TAG = "PageDelta"; 35 | 36 | PageDelta::PageDelta(SyncManager& syncman) : _syncman(syncman) 37 | { 38 | WithDisplayLock([this](){ 39 | _container_style.setPadInner(LV_STATE_DEFAULT, LV_DPX(2)); 40 | _container_style.setPad(LV_STATE_DEFAULT, LV_DPX(1), LV_DPX(1), LV_DPX(1), LV_DPX(1)); 41 | _container_style.setMargin(LV_STATE_DEFAULT, 0, 0, 0, 0); 42 | _container_style.setBorderWidth(LV_STATE_DEFAULT, 0); 43 | _container_style.setShadowWidth(LV_STATE_DEFAULT, 0); 44 | 45 | //_chart_style.setRadius(LV_STATE_DEFAULT, 0); 46 | _chart_style.setSize(LV_STATE_DEFAULT, 1); 47 | _chart_style.setLineWidth(LV_STATE_DEFAULT, 1); 48 | 49 | _page = Display::getDisplay().newPage("Dlt"); 50 | _page->addStyle(LV_PAGE_PART_SCROLLABLE, &_container_style); 51 | 52 | LVContainer* cont = new LVContainer(_page); 53 | cont->setFit(LV_FIT_PARENT/*, LV_FIT_TIGHT*/); 54 | cont->addStyle(LV_CONT_PART_MAIN, &_container_style); 55 | cont->setLayout(LV_LAYOUT_COLUMN_MID); 56 | cont->align(nullptr, LV_ALIGN_CENTER, 0, 0); 57 | cont->setDragParent(true); 58 | 59 | _overview_chart = new LVChart(cont); 60 | _overview_chart->addStyle(LV_CHART_PART_SERIES, &_chart_style); 61 | _overview_chart->setSize(315, 100); 62 | _overview_chart->setType(LV_CHART_TYPE_LINE); 63 | _overview_chart->setDragParent(true); 64 | _overview_chart->setPointCount(60); 65 | _overview_chart->setDivLineCount(3, 0); 66 | _overview_chart->setYRange(LV_CHART_AXIS_PRIMARY_Y, -10, 10); 67 | _overview_chart->setYTickLength(10, 5); 68 | _overview_chart->setYTickTexts("-10\n-5\n0\n5\n10", 5, LV_CHART_AXIS_DRAW_LAST_TICK|LV_CHART_AXIS_INVERSE_LABELS_ORDER); 69 | _overview_chart->setStylePadLeft(LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 40); 70 | _overview_chart->setStyleBorderWidth(LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 0); 71 | 72 | _min_error_series = _overview_chart->addSeries(LV_COLOR_RED); 73 | _max_error_series = _overview_chart->addSeries(LV_COLOR_RED); 74 | _avg_error_series = _overview_chart->addSeries(LV_COLOR_GREEN); 75 | 76 | _detail_chart = new LVChart(cont); 77 | _detail_chart->addStyle(LV_CHART_PART_SERIES, &_chart_style); 78 | _detail_chart->setSize(315, 100); 79 | _detail_chart->setType(LV_CHART_TYPE_LINE); 80 | _detail_chart->setDragParent(true); 81 | _detail_chart->setPointCount(60); 82 | _detail_chart->setDivLineCount(3, 0); 83 | _detail_chart->setYRange(LV_CHART_AXIS_PRIMARY_Y, -10, 10); 84 | _detail_chart->setYTickLength(10, 5); 85 | _detail_chart->setYTickTexts("-10\n-5\n0\n5\n10", 5, LV_CHART_AXIS_DRAW_LAST_TICK|LV_CHART_AXIS_INVERSE_LABELS_ORDER); 86 | _detail_chart->setStylePadLeft(LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 40); 87 | _detail_chart->setStyleBorderWidth(LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 0); 88 | 89 | _error_series = _detail_chart->addSeries(LV_COLOR_BLACK); 90 | 91 | ESP_LOGI(TAG, "creating task"); 92 | lv_task_create(task, SyncManager::OFFSET_DATA_SIZE*1000, LV_TASK_PRIO_LOW, this); 93 | }); 94 | } 95 | 96 | PageDelta::~PageDelta() 97 | { 98 | } 99 | 100 | void PageDelta::task(lv_task_t *task) 101 | { 102 | PageDelta* p = static_cast(task->user_data); 103 | p->update(); 104 | } 105 | 106 | void PageDelta::update() 107 | { 108 | if (!_syncman.isOffsetValid()) 109 | { 110 | return; 111 | } 112 | 113 | time_t now = time(nullptr); 114 | int32_t error = round(_syncman.getError()); 115 | 116 | _detail_chart->setNext(_error_series, error); 117 | 118 | if (error < _min_error || _total_count == 0) 119 | { 120 | _min_error = error; 121 | } 122 | if (error > _max_error || _total_count == 0) 123 | { 124 | _max_error = error; 125 | } 126 | 127 | _total_error += error; 128 | _total_count += 1; 129 | 130 | if ((now - _last_time) >= (60*30)) // every 30 minutes giving 30 hours on the graph 131 | { 132 | _overview_chart->setNext(_min_error_series, _min_error); 133 | _overview_chart->setNext(_max_error_series, _max_error); 134 | int avg = 0; 135 | if (_total_count > 0) 136 | { 137 | avg = _total_error / _total_count; 138 | } 139 | _overview_chart->setNext(_avg_error_series, avg); 140 | _min_error = 0; 141 | _max_error = 0; 142 | _total_count = 0; 143 | _total_error = 0; 144 | _last_time = now; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /main/PageDelta.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _PAGE_DELTA_H_ 26 | #define _PAGE_DELTA_H_ 27 | 28 | #include "SyncManager.h" 29 | #include "LVPage.h" 30 | #include "LVChart.h" 31 | #include "LVStyle.h" 32 | 33 | class PageDelta { 34 | public: 35 | explicit PageDelta(SyncManager& syncman); 36 | ~PageDelta(); 37 | 38 | PageDelta(PageDelta&) = delete; 39 | PageDelta& operator=(PageDelta&) = delete; 40 | 41 | private: 42 | void update(); 43 | static void task(lv_task_t* task); 44 | SyncManager& _syncman; 45 | LVPage* _page; 46 | LVChart* _overview_chart; 47 | lv_chart_series_t* _min_error_series; 48 | lv_chart_series_t* _max_error_series; 49 | lv_chart_series_t* _avg_error_series; 50 | LVChart* _detail_chart; 51 | lv_chart_series_t* _error_series; 52 | LVStyle _container_style; 53 | LVStyle _chart_style; 54 | 55 | int32_t _total_error = 0; 56 | int32_t _total_count = 0; 57 | int32_t _min_error = 0; 58 | int32_t _max_error = 0; 59 | time_t _last_time = 0; 60 | }; 61 | 62 | #endif // _PAGE_DELTA_H_ 63 | -------------------------------------------------------------------------------- /main/PageGPS.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "PageGPS.h" 26 | #include "Display.h" 27 | #include "WithDisplayLock.h" 28 | #include "LVContainer.h" 29 | 30 | #include "freertos/FreeRTOS.h" 31 | #include "freertos/task.h" 32 | #include "esp_log.h" 33 | 34 | static const char* TAG = "PageGPS"; 35 | 36 | PageGPS::PageGPS(GPS& gps) 37 | : _gps(gps) 38 | { 39 | WithDisplayLock([this](){ 40 | _container_style.setPadInner(LV_STATE_DEFAULT, LV_DPX(2)); 41 | _container_style.setPad(LV_STATE_DEFAULT, LV_DPX(1), LV_DPX(1), LV_DPX(1), LV_DPX(1)); 42 | _container_style.setMargin(LV_STATE_DEFAULT, 0, 0, 0, 0); 43 | _container_style.setBorderWidth(LV_STATE_DEFAULT, 0); 44 | _container_style.setShadowWidth(LV_STATE_DEFAULT, 0); 45 | 46 | _page = Display::getDisplay().newPage("GPS"); 47 | _page->addStyle(LV_PAGE_PART_SCROLLABLE, &_container_style); 48 | 49 | LVContainer* cont = new LVContainer(_page); 50 | cont->setFit(LV_FIT_PARENT/*, LV_FIT_TIGHT*/); 51 | cont->addStyle(LV_CONT_PART_MAIN, &_container_style); 52 | cont->setLayout(LV_LAYOUT_COLUMN_LEFT); 53 | cont->align(nullptr, LV_ALIGN_CENTER, 0, 0); 54 | cont->setDragParent(true); 55 | 56 | _rmc_time = new LVLabel(cont); 57 | _sats = new LVLabel(cont); 58 | _status = new LVLabel(cont); 59 | _pos = new LVLabel(cont); 60 | 61 | _psti = new LVLabel(cont); 62 | 63 | ESP_LOGI(TAG, "creating task"); 64 | lv_task_create(task, 100, LV_TASK_PRIO_LOW, this); 65 | }); 66 | } 67 | 68 | PageGPS::~PageGPS() 69 | { 70 | } 71 | 72 | void PageGPS::task(lv_task_t *task) 73 | { 74 | PageGPS* p = static_cast(task->user_data); 75 | p->update(); 76 | } 77 | 78 | void PageGPS::update() 79 | { 80 | static char buf[2048]; 81 | snprintf(buf, sizeof(buf)-1, "Sats: %d tracked: %d", _gps.getSatsTotal(), _gps.getSatsTracked()); 82 | _sats->setText(buf); 83 | 84 | snprintf(buf, sizeof(buf)-1, "Valid: %s mode: %c type: %d quality: %d", 85 | _gps.getValid() ? "yes" : "no", 86 | _gps.getMode(), _gps.getFixType(), _gps.getFixQuality()); 87 | _status->setText(buf); 88 | 89 | snprintf(buf, sizeof(buf)-1, "lat: %f lon:%f alt: %0.1f%c", _gps.getLatitude(), _gps.getLongitude(), _gps.getAltitude(), _gps.getAltitudeUnits()); 90 | _pos->setText(buf); 91 | time_t time = _gps.getRMCTime(); 92 | const char* rt = ctime_r(&time, buf); 93 | if (rt == nullptr) 94 | { 95 | strncpy(buf, "", sizeof(buf)-1); 96 | } 97 | else 98 | { 99 | buf[strlen(buf)-1] = '\0'; 100 | } 101 | _rmc_time->setText(buf); 102 | 103 | _psti->setText(_gps.getPSTI()); 104 | } 105 | -------------------------------------------------------------------------------- /main/PageGPS.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _PAGE_GPS_H_ 26 | #define _PAGE_GPS_H_ 27 | 28 | #include "GPS.h" 29 | #include "PPS.h" 30 | #include "LVPage.h" 31 | #include "LVLabel.h" 32 | #include "LVStyle.h" 33 | 34 | class PageGPS { 35 | public: 36 | explicit PageGPS(GPS& gps); 37 | ~PageGPS(); 38 | 39 | PageGPS(PageGPS&) = delete; 40 | PageGPS& operator=(PageGPS&) = delete; 41 | 42 | private: 43 | void update(); 44 | static void task(lv_task_t* task); 45 | GPS& _gps; 46 | LVPage* _page; 47 | LVLabel* _sats; 48 | LVLabel* _status; 49 | LVLabel* _pos; 50 | LVLabel* _rmc_time; 51 | 52 | LVLabel* _psti; 53 | 54 | LVStyle _container_style; 55 | }; 56 | 57 | #endif // _PAGE_GPS_H_ 58 | -------------------------------------------------------------------------------- /main/PageNTP.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "PageNTP.h" 26 | #include "Display.h" 27 | #include "WithDisplayLock.h" 28 | #include "LVContainer.h" 29 | 30 | #include "freertos/FreeRTOS.h" 31 | #include "freertos/task.h" 32 | #include "esp_log.h" 33 | 34 | static const char* TAG = "PageNTP"; 35 | 36 | enum Row 37 | { 38 | REQUESTS, 39 | RESPONSES, 40 | UPTIME, 41 | VALIDTIME, 42 | VALIDCOUNT, 43 | _NUM_ROWS 44 | }; 45 | 46 | static const char* labels[_NUM_ROWS] = {"Req:", "Resp:", "Uptime:", "Valid:", "ValidCount:"}; 47 | 48 | PageNTP::PageNTP(NTP& ntp, SyncManager& syncman) 49 | : _ntp(ntp), 50 | _syncman(syncman) 51 | { 52 | WithDisplayLock([this](){ 53 | _container_style.setPadInner(LV_STATE_DEFAULT, LV_DPX(2)); 54 | _container_style.setPad(LV_STATE_DEFAULT, LV_DPX(1), LV_DPX(1), LV_DPX(1), LV_DPX(1)); 55 | _container_style.setMargin(LV_STATE_DEFAULT, 0, 0, 0, 0); 56 | _container_style.setBorderWidth(LV_STATE_DEFAULT, 0); 57 | _container_style.setShadowWidth(LV_STATE_DEFAULT, 0); 58 | 59 | _page = Display::getDisplay().newPage("NTP"); 60 | _page->addStyle(LV_PAGE_PART_SCROLLABLE, &_container_style); 61 | 62 | LVContainer* cont = new LVContainer(_page); 63 | cont->setFit(LV_FIT_PARENT/*, LV_FIT_TIGHT*/); 64 | cont->addStyle(LV_CONT_PART_MAIN, &_container_style); 65 | cont->setLayout(LV_LAYOUT_COLUMN_LEFT); 66 | cont->align(nullptr, LV_ALIGN_CENTER, 0, 0); 67 | cont->setDragParent(true); 68 | 69 | _datetime = new LVLabel(cont); 70 | _datetime->setStyleTextFont(LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, &lv_font_montserrat_28); 71 | 72 | _table = new LVTable(cont); 73 | _table->addStyle(LV_TABLE_PART_BG, &_container_style); 74 | _table->addStyle(LV_TABLE_PART_CELL1, &_container_style); 75 | _table->setColumnCount(2); 76 | _table->setColumnWidth(0, 85); 77 | _table->setColumnWidth(1, 160); 78 | _table->setRowCount(Row::_NUM_ROWS); 79 | 80 | for (int row = 0; row < Row::_NUM_ROWS; ++row) 81 | { 82 | _table->setCellAlign(row, 0, LV_LABEL_ALIGN_RIGHT); 83 | _table->setCellValue(row, 0, labels[row]); 84 | _table->setCellAlign(row, 1, LV_LABEL_ALIGN_LEFT); 85 | } 86 | 87 | ESP_LOGI(TAG, "creating task"); 88 | lv_task_create(task, 100, LV_TASK_PRIO_LOW, this); 89 | }); 90 | } 91 | 92 | PageNTP::~PageNTP() 93 | { 94 | } 95 | 96 | void PageNTP::task(lv_task_t *task) 97 | { 98 | PageNTP* p = static_cast(task->user_data); 99 | p->update(); 100 | } 101 | 102 | static void fmtTime(const char* label, char* result, const size_t size, const time_t time, const uint32_t microseconds) 103 | { 104 | char buf[256]; 105 | struct tm tm; 106 | gmtime_r(&time, &tm); 107 | strftime(buf, sizeof(buf)-1, "%Y/%m/%d %H:%M:%S", &tm); 108 | if (microseconds < 1000000) 109 | { 110 | snprintf(result, size, "%s%s.%02u", label, buf, microseconds/10000); 111 | } 112 | else 113 | { 114 | strncpy(result, buf, size); 115 | } 116 | } 117 | 118 | static void duration(char* buf, size_t size, uint32_t seconds) 119 | { 120 | uint32_t days = seconds / 86400; 121 | seconds -= days * 86400; 122 | uint32_t hours = seconds /3600; 123 | seconds -= hours * 3600; 124 | uint32_t minutes = seconds / 60; 125 | seconds -= minutes * 60; 126 | snprintf(buf, size-1, "%ud %02uh %02um %02us", days, hours, minutes, seconds); 127 | } 128 | 129 | void PageNTP::update() 130 | { 131 | static char buf[128]; 132 | struct timeval tv; 133 | _syncman.getRTCPPSTime(&tv); 134 | fmtTime(" ", _time_buf, sizeof(buf)-1, tv.tv_sec, tv.tv_usec); 135 | _datetime->setText(_time_buf); 136 | _datetime->setStyleTextColor(LV_LABEL_PART_MAIN, LV_STATE_DEFAULT, _syncman.isValid() ? LV_COLOR_LIME : LV_COLOR_RED); 137 | 138 | snprintf(buf, sizeof(buf)-1, "%u", _ntp.getRequests()); 139 | _table->setCellValue(Row::REQUESTS, 1, buf); 140 | 141 | snprintf(buf, sizeof(buf)-1, "%u", _ntp.getResponses()); 142 | _table->setCellValue(Row::RESPONSES, 1, buf); 143 | 144 | uint32_t seconds = esp_timer_get_time() / 1000000; // uptime in seconds 145 | duration(buf, sizeof(buf), seconds); 146 | _table->setCellValue(Row::UPTIME, 1, buf); 147 | 148 | duration(buf, sizeof(buf)-1, _syncman.getValidDuration()); 149 | _table->setCellValue(Row::VALIDTIME, 1, buf); 150 | 151 | snprintf(buf, sizeof(buf)-1, "%u", _syncman.getValidCount()); 152 | _table->setCellValue(Row::VALIDCOUNT, 1, buf); 153 | } 154 | -------------------------------------------------------------------------------- /main/PageNTP.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _PAGE_NTP_H_ 26 | #define _PAGE_NTP_H_ 27 | 28 | #include "NTP.h" 29 | #include "SyncManager.h" 30 | #include "LVPage.h" 31 | #include "LVTable.h" 32 | #include "LVLabel.h" 33 | #include "LVStyle.h" 34 | 35 | class PageNTP { 36 | public: 37 | PageNTP(NTP& ntp, SyncManager& syncman); 38 | ~PageNTP(); 39 | 40 | PageNTP(PageNTP&) = delete; 41 | PageNTP& operator=(PageNTP&) = delete; 42 | 43 | private: 44 | void update(); 45 | static void task(lv_task_t* task); 46 | NTP& _ntp; 47 | SyncManager& _syncman; 48 | LVPage* _page; 49 | LVLabel* _datetime; 50 | LVTable* _table; 51 | LVStyle _container_style; 52 | char _time_buf[32] = {0}; 53 | }; 54 | 55 | #endif // _PAGE_NTP_H_ 56 | -------------------------------------------------------------------------------- /main/PagePPS.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "PagePPS.h" 26 | #include "Display.h" 27 | #include "WithDisplayLock.h" 28 | #include "LVContainer.h" 29 | 30 | #include "freertos/FreeRTOS.h" 31 | #include "freertos/task.h" 32 | #include "esp_log.h" 33 | 34 | static const char* TAG = "PagePPS"; 35 | 36 | PagePPS::PagePPS(PPS& gps_pps, PPS& rtc_pps) 37 | : _gps_pps(gps_pps), 38 | _rtc_pps(rtc_pps) 39 | { 40 | WithDisplayLock([this](){ 41 | _container_style.setPadInner(LV_STATE_DEFAULT, LV_DPX(2)); 42 | _container_style.setPad(LV_STATE_DEFAULT, LV_DPX(1), LV_DPX(1), LV_DPX(1), LV_DPX(1)); 43 | _container_style.setMargin(LV_STATE_DEFAULT, 0, 0, 0, 0); 44 | _container_style.setBorderWidth(LV_STATE_DEFAULT, 0); 45 | _container_style.setShadowWidth(LV_STATE_DEFAULT, 0); 46 | 47 | _page = Display::getDisplay().newPage("PPS"); 48 | _page->addStyle(LV_PAGE_PART_SCROLLABLE, &_container_style); 49 | 50 | LVContainer* cont = new LVContainer(_page); 51 | cont->setFit(LV_FIT_PARENT/*, LV_FIT_TIGHT*/); 52 | cont->addStyle(LV_CONT_PART_MAIN, &_container_style); 53 | cont->setLayout(LV_LAYOUT_COLUMN_LEFT); 54 | cont->align(nullptr, LV_ALIGN_CENTER, 0, 0); 55 | cont->setDragParent(true); 56 | 57 | _gps_time = new LVLabel(cont); 58 | _rtc_time = new LVLabel(cont); 59 | _gps_interval = new LVLabel(cont); 60 | _gps_minmax = new LVLabel(cont); 61 | _gps_shortlong = new LVLabel(cont); 62 | _rtc_interval = new LVLabel(cont); 63 | _rtc_minmax = new LVLabel(cont); 64 | _rtc_shortlong = new LVLabel(cont); 65 | _rtc_offset = new LVLabel(cont); 66 | 67 | ESP_LOGI(TAG, "creating task"); 68 | lv_task_create(task, 100, LV_TASK_PRIO_LOW, this); 69 | }); 70 | } 71 | 72 | PagePPS::~PagePPS() 73 | { 74 | } 75 | 76 | void PagePPS::task(lv_task_t *task) 77 | { 78 | PagePPS* p = static_cast(task->user_data); 79 | p->update(); 80 | } 81 | 82 | static void fmtTime(const char* label, char* result, const size_t size, const time_t time, const uint32_t microseconds) 83 | { 84 | char buf[256]; 85 | struct tm tm; 86 | gmtime_r(&time, &tm); 87 | strftime(buf, sizeof(buf)-1, "%Y:%m:%d %H:%M:%S", &tm); 88 | snprintf(result, size, "%s%s.%02u", label, buf, microseconds/10000); 89 | } 90 | 91 | void PagePPS::update() 92 | { 93 | static char buf[128]; 94 | struct timeval tv; 95 | _gps_pps.getTime(&tv); 96 | fmtTime("GPS PPS: ", buf, sizeof(buf)-1, tv.tv_sec, tv.tv_usec); 97 | _gps_time->setText(buf); 98 | 99 | _rtc_pps.getTime(&tv); 100 | fmtTime("RTC PPS: ", buf, sizeof(buf)-1, tv.tv_sec, tv.tv_usec); 101 | _rtc_time->setText(buf); 102 | 103 | snprintf(buf, sizeof(buf)-1, "GPS Interval: %07u", _gps_pps.getTimerInterval()); 104 | _gps_interval->setText(buf); 105 | 106 | snprintf(buf, sizeof(buf)-1, "GPS Min/Max: %06u / %07u", 107 | _gps_pps.getTimerMin(),_gps_pps.getTimerMax()); 108 | _gps_minmax->setText(buf); 109 | 110 | snprintf(buf, sizeof(buf)-1, "Short/Long: %u / %u", 111 | _gps_pps.getTimerShort(), _gps_pps.getTimerLong()); 112 | _gps_shortlong->setText(buf); 113 | 114 | snprintf(buf, sizeof(buf)-1, "RTC Interval: %07u", _rtc_pps.getTimerInterval()); 115 | _rtc_interval->setText(buf); 116 | 117 | snprintf(buf, sizeof(buf)-1, "RTC Min/Max: %06u / %07u", 118 | _rtc_pps.getTimerMin(),_rtc_pps.getTimerMax()); 119 | _rtc_minmax->setText(buf); 120 | 121 | snprintf(buf, sizeof(buf)-1, "Short/Long: %u / %u", 122 | _rtc_pps.getTimerShort(), _rtc_pps.getTimerLong()); 123 | _rtc_shortlong->setText(buf); 124 | 125 | snprintf(buf, sizeof(buf)-1, "RTC Offset: %d", _rtc_pps.getOffset()); 126 | _rtc_offset->setText(buf); 127 | 128 | } 129 | -------------------------------------------------------------------------------- /main/PagePPS.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _PAGE_PPS_H_ 26 | #define _PAGE_PPS_H_ 27 | 28 | #include "PPS.h" 29 | #include "LVPage.h" 30 | #include "LVLabel.h" 31 | #include "LVStyle.h" 32 | 33 | class PagePPS { 34 | public: 35 | PagePPS(PPS& gps_pps, PPS& rtc_pps); 36 | ~PagePPS(); 37 | 38 | PagePPS(PagePPS&) = delete; 39 | PagePPS& operator=(PagePPS&) = delete; 40 | 41 | private: 42 | void update(); 43 | static void task(lv_task_t* task); 44 | PPS& _gps_pps; 45 | PPS& _rtc_pps; 46 | 47 | LVPage* _page; 48 | LVLabel* _gps_time; 49 | LVLabel* _rtc_time; 50 | LVLabel* _gps_interval; 51 | LVLabel* _rtc_interval; 52 | LVLabel* _gps_minmax; 53 | LVLabel* _gps_shortlong; 54 | LVLabel* _rtc_minmax; 55 | LVLabel* _rtc_shortlong; 56 | LVLabel* _rtc_offset; 57 | LVStyle _container_style; 58 | }; 59 | 60 | #endif // _PAGE_GPS_H_ 61 | -------------------------------------------------------------------------------- /main/PageSats.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "PageSats.h" 26 | #include "Display.h" 27 | #include "WithDisplayLock.h" 28 | #include "LVContainer.h" 29 | 30 | #include "freertos/FreeRTOS.h" 31 | #include "freertos/task.h" 32 | #include "esp_log.h" 33 | 34 | static const char* TAG = "PageSats"; 35 | 36 | PageSats::PageSats(GPS& gps) : _gps(gps) 37 | { 38 | WithDisplayLock([this](){ 39 | _container_style.setPadInner(LV_STATE_DEFAULT, LV_DPX(2)); 40 | _container_style.setPad(LV_STATE_DEFAULT, LV_DPX(1), LV_DPX(1), LV_DPX(1), LV_DPX(1)); 41 | _container_style.setMargin(LV_STATE_DEFAULT, 0, 0, 0, 0); 42 | _container_style.setBorderWidth(LV_STATE_DEFAULT, 0); 43 | _container_style.setShadowWidth(LV_STATE_DEFAULT, 0); 44 | 45 | //_chart_style.setRadius(LV_STATE_DEFAULT, 0); 46 | _chart_style.setSize(LV_STATE_DEFAULT, 1); 47 | _chart_style.setLineWidth(LV_STATE_DEFAULT, 1); 48 | 49 | _page = Display::getDisplay().newPage("Sats"); 50 | _page->addStyle(LV_PAGE_PART_SCROLLABLE, &_container_style); 51 | 52 | LVContainer* cont = new LVContainer(_page); 53 | cont->setFit(LV_FIT_PARENT/*, LV_FIT_TIGHT*/); 54 | cont->addStyle(LV_CONT_PART_MAIN, &_container_style); 55 | cont->setLayout(LV_LAYOUT_COLUMN_MID); 56 | cont->align(nullptr, LV_ALIGN_CENTER, 0, 0); 57 | cont->setDragParent(true); 58 | 59 | _chart = new LVChart(cont); 60 | _chart->addStyle(LV_CHART_PART_SERIES, &_chart_style); 61 | _chart->setSize(315, 200); 62 | _chart->setType(LV_CHART_TYPE_LINE); 63 | _chart->setDragParent(true); 64 | _chart->setPointCount(60); 65 | _chart->setDivLineCount(4, 0); 66 | _chart->setYRange(LV_CHART_AXIS_PRIMARY_Y, 0, 25); 67 | _chart->setYTickLength(10, 5); 68 | _chart->setYTickTexts("0\n5\n10\n15\n20\n25", 5, LV_CHART_AXIS_DRAW_LAST_TICK|LV_CHART_AXIS_INVERSE_LABELS_ORDER); 69 | _chart->setStylePadLeft(LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 35); 70 | _chart->setStyleBorderWidth(LV_OBJ_PART_MAIN, LV_STATE_DEFAULT, 0); 71 | 72 | _tracked_max_series = _chart->addSeries(LV_COLOR_LIME); 73 | _tracked_min_series = _chart->addSeries(LV_COLOR_ORANGE); 74 | 75 | ESP_LOGI(TAG, "creating task"); 76 | lv_task_create(task, 500, LV_TASK_PRIO_LOW, this); 77 | }); 78 | } 79 | 80 | PageSats::~PageSats() 81 | { 82 | } 83 | 84 | void PageSats::task(lv_task_t *task) 85 | { 86 | PageSats* p = static_cast(task->user_data); 87 | p->update(); 88 | } 89 | 90 | void PageSats::update() 91 | { 92 | int tracked = _gps.getSatsTracked(); 93 | if (tracked > _tracked_max) 94 | { 95 | _tracked_max = tracked; 96 | } 97 | if (tracked < _tracked_min) 98 | { 99 | _tracked_min = tracked; 100 | } 101 | 102 | time_t now = time(nullptr); 103 | // update the chart once a minute 104 | if (now > (_last_time + 60)) 105 | { 106 | _chart->setNext(_tracked_min_series, _tracked_min); 107 | _chart->setNext(_tracked_max_series, _tracked_max); 108 | _tracked_min = tracked; 109 | _tracked_max = tracked; 110 | _last_time = now; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /main/PageSats.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _PAGE_SATS_H_ 26 | #define _PAGE_SATS_H_ 27 | 28 | #include "GPS.h" 29 | #include "LVPage.h" 30 | #include "LVChart.h" 31 | #include "LVStyle.h" 32 | 33 | class PageSats { 34 | public: 35 | explicit PageSats(GPS& gps); 36 | ~PageSats(); 37 | 38 | PageSats(PageSats&) = delete; 39 | PageSats& operator=(PageSats&) = delete; 40 | 41 | private: 42 | void update(); 43 | static void task(lv_task_t* task); 44 | GPS& _gps; 45 | LVPage* _page; 46 | LVChart* _chart; 47 | lv_chart_series_t* _tracked_max_series; 48 | lv_chart_series_t* _tracked_min_series; 49 | LVStyle _container_style; 50 | LVStyle _chart_style; 51 | 52 | uint16_t _tracked_max = 0; 53 | uint16_t _tracked_min = 0; 54 | 55 | time_t _last_time = 0; 56 | }; 57 | 58 | #endif // _PAGE_SATS_H_ 59 | -------------------------------------------------------------------------------- /main/PageSync.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "PageSync.h" 26 | #include "Display.h" 27 | #include "WithDisplayLock.h" 28 | #include "LVContainer.h" 29 | 30 | #include "freertos/FreeRTOS.h" 31 | #include "freertos/task.h" 32 | #include "esp_log.h" 33 | 34 | static const char* TAG = "PageSync"; 35 | 36 | enum Row 37 | { 38 | RTC_TIME = 0, 39 | GPS_TIME, 40 | RTC_PPS_TIME, 41 | GPS_PPS_TIME, 42 | OFFSET, 43 | ERROR, 44 | INTEGRAL, 45 | OUTPUT, 46 | _NUM_ROWS 47 | }; 48 | 49 | static const char* labels[_NUM_ROWS] = {"RTC:", "GPS:", "RTC PPS:", "GPS PPS:", "Offset:", "Error:", "Integral:", "Output:"}; 50 | 51 | PageSync::PageSync(SyncManager& syncman) 52 | : _syncman(syncman) 53 | { 54 | WithDisplayLock([this](){ 55 | _container_style.setPadInner(LV_STATE_DEFAULT, LV_DPX(2)); 56 | _container_style.setPad(LV_STATE_DEFAULT, LV_DPX(1), LV_DPX(1), LV_DPX(1), LV_DPX(1)); 57 | _container_style.setMargin(LV_STATE_DEFAULT, 0, 0, 0, 0); 58 | _container_style.setBorderWidth(LV_STATE_DEFAULT, 0); 59 | _container_style.setShadowWidth(LV_STATE_DEFAULT, 0); 60 | 61 | _page = Display::getDisplay().newPage("Syn"); 62 | _page->addStyle(LV_PAGE_PART_SCROLLABLE, &_container_style); 63 | 64 | LVContainer* cont = new LVContainer(_page); 65 | cont->setFit(LV_FIT_PARENT/*, LV_FIT_TIGHT*/); 66 | cont->addStyle(LV_CONT_PART_MAIN, &_container_style); 67 | cont->setLayout(LV_LAYOUT_COLUMN_LEFT); 68 | cont->align(nullptr, LV_ALIGN_CENTER, 0, 0); 69 | cont->setDragParent(true); 70 | 71 | _table = new LVTable(cont); 72 | _table->addStyle(LV_TABLE_PART_BG, &_container_style); 73 | _table->addStyle(LV_TABLE_PART_CELL1, &_container_style); 74 | _table->setColumnCount(2); 75 | _table->setColumnWidth(0, 80); 76 | _table->setColumnWidth(1, 160); 77 | _table->setRowCount(Row::_NUM_ROWS); 78 | 79 | for (int row = 0; row < Row::_NUM_ROWS; ++row) 80 | { 81 | _table->setCellAlign(row, 0, LV_LABEL_ALIGN_RIGHT); 82 | _table->setCellValue(row, 0, labels[row]); 83 | _table->setCellAlign(row, 1, LV_LABEL_ALIGN_LEFT); 84 | } 85 | 86 | ESP_LOGI(TAG, "creating task"); 87 | lv_task_create(task, 100, LV_TASK_PRIO_LOW, this); 88 | }); 89 | } 90 | 91 | PageSync::~PageSync() 92 | { 93 | } 94 | 95 | void PageSync::task(lv_task_t *task) 96 | { 97 | PageSync* p = static_cast(task->user_data); 98 | p->update(); 99 | } 100 | 101 | static void fmtTime(const char* label, char* result, const size_t size, const time_t time, const uint32_t microseconds) 102 | { 103 | char buf[256]; 104 | struct tm tm; 105 | gmtime_r(&time, &tm); 106 | strftime(buf, sizeof(buf)-1, "%Y:%m:%d %H:%M:%S", &tm); 107 | if (microseconds < 1000000) 108 | { 109 | snprintf(result, size, "%s%s.%02u", label, buf, microseconds/10000); 110 | } 111 | else 112 | { 113 | strncpy(result, buf, size); 114 | } 115 | } 116 | 117 | void PageSync::update() 118 | { 119 | static char buf[128]; 120 | struct timeval tv; 121 | time_t now = _syncman.getRTCTime(); 122 | fmtTime("", buf, sizeof(buf)-1, now, 0xffffffff); 123 | _table->setCellValue(Row::RTC_TIME, 1, buf); 124 | 125 | now = _syncman.getGPSTime(); 126 | fmtTime("", buf, sizeof(buf)-1, now, 0xffffffff); 127 | _table->setCellValue(Row::GPS_TIME, 1, buf); 128 | 129 | _syncman.getRTCPPSTime(&tv); 130 | fmtTime("", buf, sizeof(buf)-1, tv.tv_sec, tv.tv_usec); 131 | _table->setCellValue(Row::RTC_PPS_TIME, 1, buf); 132 | 133 | _syncman.getGPSPPSTime(&tv); 134 | fmtTime("", buf, sizeof(buf)-1, tv.tv_sec, tv.tv_usec); 135 | _table->setCellValue(Row::GPS_PPS_TIME, 1, buf); 136 | 137 | snprintf(buf, sizeof(buf)-1, "%0.3f", _syncman.getOffset()); 138 | _table->setCellValue(Row::OFFSET, 1, buf); 139 | 140 | snprintf(buf, sizeof(buf)-1, "%0.3f", _syncman.getPreviousError()); 141 | _table->setCellValue(Row::ERROR, 1, buf); 142 | 143 | snprintf(buf, sizeof(buf)-1, "%0.3f", _syncman.getIntegral()); 144 | _table->setCellValue(Row::INTEGRAL, 1, buf); 145 | 146 | snprintf(buf, sizeof(buf)-1, "%d", _syncman.getOutput()); 147 | _table->setCellValue(Row::OUTPUT, 1, buf); 148 | } 149 | -------------------------------------------------------------------------------- /main/PageSync.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _PAGE_SYNC_H_ 26 | #define _PAGE_SYNC_H_ 27 | 28 | #include "SyncManager.h" 29 | #include "LVPage.h" 30 | #include "LVTable.h" 31 | #include "LVLabel.h" 32 | #include "LVStyle.h" 33 | 34 | class PageSync { 35 | public: 36 | explicit PageSync(SyncManager& syncman); 37 | ~PageSync(); 38 | 39 | PageSync(PageSync&) = delete; 40 | PageSync& operator=(PageSync&) = delete; 41 | 42 | private: 43 | void update(); 44 | static void task(lv_task_t* task); 45 | SyncManager& _syncman; 46 | LVPage* _page; 47 | LVTable* _table; 48 | LVStyle _container_style; 49 | }; 50 | 51 | #endif // _PAGE_SYNC_H_ 52 | -------------------------------------------------------------------------------- /main/SyncManager.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "SyncManager.h" 26 | //#define LOG_LOCAL_LEVEL ESP_LOG_DEBUG 27 | #include "esp_log.h" 28 | 29 | #if defined(CONFIG_GPSNTP_RTC_DRIFT_MAX) 30 | #define RTC_DRIFT_MAX CONFIG_GPSNTP_RTC_DRIFT_MAX 31 | #else 32 | #define RTC_DRIFT_MAX 500 33 | #endif 34 | 35 | #ifndef SYNC_TASK_PRI 36 | #define SYNC_TASK_PRI configMAX_PRIORITIES 37 | #endif 38 | 39 | #ifndef SYNC_TASK_CORE 40 | #define SYNC_TASK_CORE 1 41 | #endif 42 | 43 | #define LATENCY_PIN 2 44 | 45 | static const char* TAG = "SyncManager"; 46 | 47 | SyncManager::SyncManager(GPS& gps, DS3231& rtc, PPS& gpspps, PPS& rtcpps) 48 | : _gps(gps), 49 | _rtc(rtc), 50 | _gpspps(gpspps), 51 | _rtcpps(rtcpps) 52 | { 53 | } 54 | 55 | bool SyncManager::begin() 56 | { 57 | ESP_LOGI(TAG, "::begin create Sync task at priority %d core %d", SYNC_TASK_PRI, SYNC_TASK_CORE); 58 | xTaskCreatePinnedToCore(task, "Sync", 4096, this, SYNC_TASK_PRI, &_task, SYNC_TASK_CORE); 59 | return true; 60 | } 61 | 62 | time_t SyncManager::getGPSTime() 63 | { 64 | return _gps.getRMCTime(); 65 | } 66 | 67 | time_t SyncManager::getRTCTime() 68 | { 69 | return _rtc_time; 70 | } 71 | 72 | void SyncManager::getRTCPPSTime(struct timeval* tv) 73 | { 74 | _rtcpps.getTime(tv); 75 | } 76 | 77 | void SyncManager::getGPSPPSTime(struct timeval* tv) 78 | { 79 | _gpspps.getTime(tv); 80 | } 81 | 82 | float SyncManager::getError() 83 | { 84 | return _target - (float)getOffset(); 85 | } 86 | 87 | float SyncManager::getPreviousError() 88 | { 89 | return _previous_error; 90 | } 91 | 92 | float SyncManager::getIntegral() 93 | { 94 | return _integral; 95 | } 96 | 97 | uint32_t SyncManager::getUptime() 98 | { 99 | return (esp_timer_get_time() / 1000000); 100 | } 101 | 102 | void SyncManager::recordOffset() 103 | { 104 | static time_t last_rtc = 0; 105 | static time_t last_gps = 0; 106 | time_t rtc_time = _rtcpps.getTime(nullptr); 107 | time_t gps_time = _gpspps.getTime(nullptr); 108 | 109 | // we want a change in both second counters before we take a new sample 110 | if (gps_time == last_gps || rtc_time == last_rtc) 111 | { 112 | return; 113 | } 114 | 115 | last_rtc = rtc_time; 116 | last_gps = gps_time; 117 | 118 | _offset_data[_offset_index++] = _rtcpps.getOffset(); 119 | 120 | if (_offset_index >= OFFSET_DATA_SIZE) 121 | { 122 | _offset_index = 0; 123 | } 124 | 125 | if (_offset_count < OFFSET_DATA_SIZE) 126 | { 127 | _offset_count += 1; 128 | } 129 | 130 | uint32_t gps_interval = _gpspps.getTimerInterval(); 131 | uint32_t rtc_interval = _rtcpps.getTimerInterval(); 132 | if (rtc_interval < 999950 || rtc_interval > 1000050) 133 | { 134 | ESP_LOGW(TAG, "::recordOffset: RTC interval out of range: %u", rtc_interval); 135 | } 136 | if (gps_interval < 999950 || gps_interval > 1000050) 137 | { 138 | ESP_LOGW(TAG, "::recordOffset: GPS interval out of range: %u", gps_interval); 139 | } 140 | } 141 | 142 | bool SyncManager::isOffsetValid() 143 | { 144 | return _offset_count == OFFSET_DATA_SIZE; 145 | } 146 | 147 | float SyncManager::getBias() 148 | { 149 | return _bias; 150 | } 151 | 152 | void SyncManager::setBias(float bias) 153 | { 154 | ESP_LOGI(TAG, "setBias: %0f", bias); 155 | _bias = bias; 156 | resetOffset(); 157 | } 158 | 159 | float SyncManager::getTarget() 160 | { 161 | return _target; 162 | } 163 | 164 | void SyncManager::setTarget(float target) 165 | { 166 | ESP_LOGI(TAG, "setTarget: %0f", target); 167 | _target = target; 168 | resetOffset(); 169 | } 170 | 171 | bool SyncManager::isValid() 172 | { 173 | return _gps.getValid(); 174 | } 175 | 176 | uint32_t SyncManager::getValidDuration() 177 | { 178 | return _gps.getValidDuration(); 179 | } 180 | 181 | uint32_t SyncManager::getValidCount() 182 | { 183 | return _gps.getValidCount(); 184 | } 185 | 186 | int8_t SyncManager::getOutput() 187 | { 188 | return _output; 189 | } 190 | 191 | /** 192 | * return the average offset. 0 is returnerd if the offset data is not full. 193 | */ 194 | float SyncManager::getOffset(int32_t* minp, int32_t* maxp) 195 | { 196 | if (_offset_count < OFFSET_DATA_SIZE) 197 | { 198 | return 0; 199 | } 200 | 201 | float total = _offset_data[0]; 202 | int32_t min_offset = _offset_data[0]; 203 | int32_t max_offset = _offset_data[0]; 204 | 205 | for(uint32_t i = 1; i < _offset_count; ++i) 206 | { 207 | total += _offset_data[i]; 208 | if (_offset_data[i] < min_offset) 209 | { 210 | min_offset = _offset_data[i]; 211 | } 212 | if (_offset_data[i] > max_offset) 213 | { 214 | max_offset = _offset_data[i]; 215 | } 216 | } 217 | // throw out the min and max values 218 | total -= min_offset; 219 | total -= max_offset; 220 | float offset = total / ((float)OFFSET_DATA_SIZE-2); 221 | if (minp != nullptr) 222 | { 223 | *minp = min_offset; 224 | } 225 | if (maxp != nullptr) 226 | { 227 | *maxp = max_offset; 228 | } 229 | return offset; 230 | } 231 | 232 | void SyncManager::resetOffset() 233 | { 234 | _offset_index = 0; 235 | _offset_count = 0; 236 | // reset PID controler as its invalid when we set the or finish adjusting 237 | _drift_start_time = 0; 238 | _integral = 0; 239 | _previous_error = 0; 240 | } 241 | 242 | void SyncManager::manageDrift(float offset) 243 | { 244 | time_t now = time(nullptr); // we only need simple incrementing seconds 245 | 246 | // 247 | // if drift start time is 0 then we have no initial sample take it now 248 | // 249 | if (_drift_start_time == 0) 250 | { 251 | // only initialize on a non-zero offset 252 | if (offset != 0) 253 | { 254 | _drift_start_time = now; 255 | } 256 | return; 257 | } 258 | 259 | 260 | uint32_t interval = now - _drift_start_time; 261 | if (interval >= PID_INTERVAL) 262 | { 263 | float error = _target - (float)offset; 264 | _integral += error; 265 | // limit the integral to affecting the output by 64 max (thats about 6-7 PPM) 266 | // Note that the integral is what builds up to compensate for any natural drift 267 | // in the rtc, with a ds3231 (w/temperature controled oscillator) thats a max 268 | // of 2ppm. Also 64 is half the max we can adjust in either direction 269 | if ((_integral*_Ki) > 64.0) 270 | { 271 | _integral = 64.0/_Ki; 272 | } 273 | else if ((_integral*_Ki) < -64.0) 274 | { 275 | _integral = -64.0/_Ki; 276 | } 277 | float derivative = error - _previous_error; 278 | _previous_error = error; 279 | float output = _Kp*error + _Ki*_integral + _Kd*derivative + _bias; 280 | output = round(output); 281 | if (output > 127) 282 | { 283 | output = 127; 284 | } 285 | if (output < -127) 286 | { 287 | output = -127; 288 | } 289 | 290 | int32_t min_offset = _offset_data[0]; 291 | int32_t max_offset = _offset_data[0]; 292 | for (size_t i = 1; i < OFFSET_DATA_SIZE; ++i) 293 | { 294 | if (_offset_data[i] > max_offset) 295 | { 296 | max_offset = _offset_data[i]; 297 | } 298 | if (_offset_data[i] < min_offset) 299 | { 300 | min_offset = _offset_data[i]; 301 | } 302 | 303 | } 304 | 305 | if (_rtc.getAgeOffset() != (int8_t)output) 306 | { 307 | _output = output; 308 | _rtc.setAgeOffset((int8_t)output); 309 | ESP_LOGI(TAG, "::manageDrift: target=%0.1f offset=%0.1f/%d/%d error=%0.1f i=%0.1f d=%0.1f bias=%0.1f out=%d", 310 | _target, offset, min_offset, max_offset, error, _integral, derivative, _bias, (int8_t)output); 311 | } 312 | 313 | _drift_start_time = now; 314 | } 315 | } 316 | 317 | void SyncManager::process() 318 | { 319 | // update value of RTC display (we are the only thread allowed to talk in i2c) 320 | struct tm tm; 321 | _rtc.getTime(&tm); 322 | _rtc_time = mktime(&tm); 323 | 324 | // if the GPS is not valid then reset the offset and return 325 | if (!_gps.getValid()) 326 | { 327 | resetOffset(); 328 | return; 329 | } 330 | 331 | recordOffset(); 332 | float offset = getOffset(); 333 | 334 | struct timeval gps_tv; 335 | struct timeval rtc_tv; 336 | _gpspps.getTime(&gps_tv); 337 | _rtcpps.getTime(&rtc_tv); 338 | 339 | uint32_t interval = gps_tv.tv_sec - _last_time; 340 | 341 | // ~10 sec but only if GPS is valid and not too close to the start or end of a second! 342 | if (gps_tv.tv_usec > 800000 343 | && gps_tv.tv_usec < 900000 344 | && interval > 10) 345 | { 346 | // since we are almost at teh end of a second the gps message for the current sencond should have arrived 347 | // and we can compare it with the gps_pps second counter and update the counter if different. 348 | time_t gps_seconds = _gps.getRMCTime(); 349 | if (gps_seconds != gps_tv.tv_sec) 350 | { 351 | ESP_LOGW(TAG, "updating GPS PPS Time PPS %ld -> %ld (%+ld seconds)", 352 | gps_tv.tv_sec, gps_seconds, gps_seconds-gps_tv.tv_sec); 353 | _gpspps.setTime(gps_seconds); 354 | gps_tv.tv_sec = gps_seconds; 355 | } 356 | ESP_LOGV(TAG, "pps offset %0.3f", offset); 357 | _last_time = gps_tv.tv_sec; 358 | 359 | if (abs(offset) > RTC_DRIFT_MAX || gps_tv.tv_sec != rtc_tv.tv_sec) 360 | { 361 | setTime(offset); 362 | ESP_LOGW(TAG, "time correction happened! PPS offset=%0.3fus gps_time=%ld rtc_time=%ld (%+ld seconds)", 363 | offset, gps_tv.tv_sec, rtc_tv.tv_sec, gps_tv.tv_sec-rtc_tv.tv_sec); 364 | struct timeval tv; 365 | _rtcpps.getTime(&tv); 366 | settimeofday(&tv, nullptr); 367 | resetOffset(); 368 | } 369 | return; 370 | } 371 | 372 | manageDrift(offset); 373 | } 374 | 375 | void SyncManager::task(void* data) 376 | { 377 | ESP_LOGI(TAG, "::task - starting!"); 378 | SyncManager* syncman = static_cast(data); 379 | //we dont start for a few seconds so that times can be set and initial seconds and offsets are computed 380 | 381 | vTaskDelay(pdMS_TO_TICKS(5000)); 382 | 383 | while(true) 384 | { 385 | syncman->process(); 386 | vTaskDelay(pdMS_TO_TICKS(10)); 387 | } 388 | ESP_LOGE(TAG, "::task - terminating (should never happen)!"); 389 | vTaskDelete(nullptr); 390 | } 391 | 392 | void SyncManager::setTime(int32_t delta) 393 | { 394 | uint32_t target = 1000000 - 200; // just shy of the second mark to compensate for time to write seconds 395 | struct timeval tv; 396 | uint32_t loops = 0; 397 | do { 398 | _gpspps.getTime(&tv); 399 | ++loops; 400 | } while (tv.tv_usec < target); // busy wait for microseconds! 401 | 402 | #ifdef SYNC_LATENCY_OUTPUT 403 | gpio_set_level(LATENCY_PIN, 1); 404 | #endif 405 | 406 | // we are targeting the next second 407 | tv.tv_sec += 1; 408 | 409 | // disable time incrementing in the RTC PPS so we dont accidentally increment 410 | // then set the RTC PPS counter time. It wil be re-enabled after the RTC has been set 411 | _rtcpps.setDisable(true); 412 | _rtcpps.setTime(tv.tv_sec); 413 | 414 | struct tm* tm = gmtime(&tv.tv_sec); 415 | if (!_rtc.setTime(tm)) 416 | { 417 | #ifdef SYNC_LATENCY_OUTPUT 418 | gpio_set_level(LATENCY_PIN, 0); 419 | #endif 420 | ESP_LOGE(TAG, "setTime: failed to set time for DS3231"); 421 | return; 422 | } 423 | _rtcpps.setDisable(false); 424 | 425 | #ifdef SYNC_LATENCY_OUTPUT 426 | gpio_set_level(LATENCY_PIN, 0); 427 | #endif 428 | 429 | ESP_LOGI(TAG, "setTime: success setting time! microsecond value=%ld loops=%u", tv.tv_usec, loops); 430 | } 431 | -------------------------------------------------------------------------------- /main/SyncManager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _SYNC_MANAGER_H 26 | #define _SYNC_MANAGER_H 27 | #include "freertos/FreeRTOS.h" 28 | #include "freertos/semphr.h" 29 | #include "GPS.h" 30 | #include "PPS.h" 31 | #include "DS3231.h" 32 | 33 | class SyncManager { 34 | public: 35 | SyncManager(GPS& gps, DS3231& rtc, PPS& gpspps, PPS& rtcpps); 36 | bool begin(); 37 | time_t getGPSTime(); 38 | time_t getRTCTime(); 39 | void getRTCPPSTime(struct timeval* tv); 40 | void getGPSPPSTime(struct timeval* tv); 41 | bool isOffsetValid(); 42 | float getOffset(int32_t* minp=nullptr, int32_t* maxp = nullptr); 43 | float getError(); 44 | float getPreviousError(); 45 | float getIntegral(); 46 | uint32_t getUptime(); 47 | float getBias(); 48 | void setBias(float bias); 49 | float getTarget(); 50 | void setTarget(float target); 51 | bool isValid(); // is GPS valid 52 | uint32_t getValidDuration(); 53 | uint32_t getValidCount(); 54 | int8_t getOutput(); 55 | static const uint32_t PID_INTERVAL = 1; 56 | static const uint32_t OFFSET_DATA_SIZE = 10; 57 | 58 | private: 59 | float _Kp = 3.2; 60 | float _Ki = 0.1; 61 | float _Kd = 0.8; 62 | 63 | int32_t _offset_data[OFFSET_DATA_SIZE] = {0}; 64 | GPS& _gps; 65 | DS3231& _rtc; 66 | PPS& _gpspps; 67 | PPS& _rtcpps; 68 | TaskHandle_t _task; 69 | 70 | // 71 | // Target is an offset in microseconds that we try to keep the RTC PPS from teh GPS PPS 72 | // 73 | float _target = 0.0; 74 | // 75 | // We can optionally bias the output to compensate for natural drift from the RTC. This 76 | // makes initial syncing faster as the integral does not need to build up to compensate. 77 | // A good value for that can be found by setting bias to zero, letting Sync match the target. 78 | // than take the synced integral value and multiply it by Ki. 79 | // 80 | float _bias = 0.0; 81 | 82 | volatile time_t _last_time = 0; 83 | volatile time_t _rtc_time = 0; 84 | time_t _drift_start_time = 0; // start of drift timeing (if 0 means no initial sample) 85 | uint32_t _offset_index = 0; 86 | uint32_t _offset_count = 0; 87 | float _integral = 0.0; 88 | float _previous_error = 0.0; 89 | int8_t _output = 0; 90 | void recordOffset(); 91 | void resetOffset(); 92 | void manageDrift(float offset); 93 | void process(); 94 | void setTime(int32_t delta); 95 | static void task(void* data); 96 | 97 | }; 98 | 99 | #endif // _SYNC_MANAGER_H -------------------------------------------------------------------------------- /main/WithDisplayLock.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include "WithDisplayLock.h" 26 | #include "Display.h" 27 | #include "esp_log.h" 28 | 29 | static const char* TAG = "WithDisplayLock"; 30 | 31 | WithDisplayLock::WithDisplayLock(const std::function& got_lock, const std::function& got_no_lock) 32 | { 33 | _locked = Display::getDisplay().lock(1000); 34 | if (_locked) 35 | { 36 | ESP_LOGD(TAG, "calling got_lock exists=%d", got_lock != nullptr); 37 | got_lock(); 38 | } 39 | else if (got_no_lock) 40 | { 41 | ESP_LOGD(TAG, "got_no_lock!"); 42 | got_no_lock(); 43 | } 44 | } 45 | 46 | WithDisplayLock::WithDisplayLock() 47 | { 48 | while(true) 49 | { 50 | _locked = Display::getDisplay().lock(1000); 51 | if (_locked) 52 | { 53 | return; 54 | } 55 | ESP_LOGE(TAG, "failed to get lock, will retry!"); 56 | } 57 | } 58 | 59 | WithDisplayLock::~WithDisplayLock() 60 | { 61 | if (_locked) 62 | { 63 | ESP_LOGD(TAG, "unlocking!"); 64 | Display::getDisplay().unlock(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /main/WithDisplayLock.h: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef _WITH_DISPLAY_LOCK_H 26 | #define _WITH_DISPLAY_LOCK_H 27 | #include 28 | 29 | class WithDisplayLock { 30 | public: 31 | explicit WithDisplayLock(const std::function& got_lock, const std::function& got_no_lock = nullptr); 32 | WithDisplayLock(); 33 | ~WithDisplayLock(); 34 | 35 | private: 36 | bool _locked = false; 37 | }; 38 | 39 | #endif // _WITH_DISPLAY_LOCK_H 40 | -------------------------------------------------------------------------------- /main/highint5.S: -------------------------------------------------------------------------------- 1 | /* 2 | * MIT License 3 | * 4 | * Copyright (c) 2020 Christopher B. Liebman 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in all 14 | * copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include "freertos/xtensa_context.h" 29 | #include "esp_private/panic_reason.h" 30 | #include "sdkconfig.h" 31 | #include "soc/soc.h" 32 | #include "soc/gpio_reg.h" 33 | #include "soc/timer_group_reg.h" 34 | 35 | #define L5_INTR_A2_OFFSET 0 36 | #define L5_INTR_A3_OFFSET 4 37 | #define L5_INTR_A4_OFFSET 8 38 | #define L5_INTR_A5_OFFSET 12 39 | #define L5_INTR_A6_OFFSET 16 40 | #define L5_INTR_SAR_OFFSET 20 41 | #define L5_INTR_STACK_SIZE 24 42 | .data 43 | _l5_intr_stack: 44 | .space L5_INTR_STACK_SIZE 45 | 46 | /* GPIO interrupt register offsets */ 47 | #define STATUS_OFFSET 0 48 | #define W1TC_OFFSET 8 49 | 50 | #define LATENCY_GPIO_NUM 2 51 | #define LATENCY_GPIO_BIT (1< 3600) // 1 hr 259 | { 260 | if (is_on) 261 | { 262 | ESP_LOGI(TAG, "turning OFF backlight"); 263 | gpio_set_level(TFT_LED_PIN, 0); 264 | is_on = false; 265 | } 266 | } 267 | else if (!is_on) 268 | { 269 | ESP_LOGI(TAG, "turning ON backlight"); 270 | gpio_set_level(TFT_LED_PIN, 1); 271 | is_on = true; 272 | } 273 | 274 | vTaskDelay(pdMS_TO_TICKS(100)); 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /partitions.csv: -------------------------------------------------------------------------------- 1 | # ESP-IDF Partition Table 2 | # Name, Type, SubType, Offset, Size, Flags 3 | nvs, data, nvs, 0x9000, 0x6000, 4 | phy_init, data, phy, 0xf000, 0x1000, 5 | factory, app, factory, 0x10000, 1500K, 6 | config, data, nvs, , 0x8000, 7 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | [platformio] 2 | default_envs = default 3 | src_dir = main 4 | extra_configs = 5 | local.ini 6 | 7 | [env] 8 | board = esp32dev 9 | framework = espidf 10 | lib_ldf_mode = off 11 | board_build.partitions = partitions.csv 12 | 13 | build_type = debug 14 | 15 | [env:default] 16 | platform = espressif32@3.0.0 17 | -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PlatformIO Unit Testing and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PlatformIO Unit Testing: 11 | - https://docs.platformio.org/page/plus/unit-testing.html 12 | --------------------------------------------------------------------------------