├── .codespellrc ├── .github ├── dependabot.yml └── workflows │ ├── check-arduino.yml │ ├── compile-examples.yml │ ├── report-size-deltas.yml │ ├── spell-check.yml │ └── sync-labels.yml ├── CHANGELOG ├── LICENSE.txt ├── NTPClient.cpp ├── NTPClient.h ├── README.md ├── examples ├── Advanced │ └── Advanced.ino ├── Basic │ └── Basic.ino └── IsTimeSet │ └── IsTimeSet.ino ├── keywords.txt └── library.properties /.codespellrc: -------------------------------------------------------------------------------- 1 | # See: https://github.com/codespell-project/codespell#using-a-config-file 2 | [codespell] 3 | # In the event of a false positive, add the problematic word, in all lowercase, to a comma-separated list here: 4 | ignore-words-list = , 5 | check-filenames = 6 | check-hidden = 7 | skip = ./.git 8 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # See: https://docs.github.com/en/github/administering-a-repository/configuration-options-for-dependency-updates#about-the-dependabotyml-file 2 | version: 2 3 | 4 | updates: 5 | # Configure check for outdated GitHub Actions actions in workflows. 6 | # See: https://docs.github.com/en/github/administering-a-repository/keeping-your-actions-up-to-date-with-dependabot 7 | - package-ecosystem: github-actions 8 | directory: / # Check the repository's workflows under /.github/workflows/ 9 | schedule: 10 | interval: daily 11 | -------------------------------------------------------------------------------- /.github/workflows/check-arduino.yml: -------------------------------------------------------------------------------- 1 | name: Check Arduino 2 | 3 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | pull_request: 7 | schedule: 8 | # Run every Tuesday at 8 AM UTC to catch breakage caused by new rules added to Arduino Lint. 9 | - cron: "0 8 * * TUE" 10 | workflow_dispatch: 11 | repository_dispatch: 12 | 13 | jobs: 14 | lint: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | 21 | - name: Arduino Lint 22 | uses: arduino/arduino-lint-action@v2 23 | with: 24 | compliance: specification 25 | library-manager: update 26 | # Always use this setting for official repositories. Remove for 3rd party projects. 27 | official: true 28 | project-type: library 29 | -------------------------------------------------------------------------------- /.github/workflows/compile-examples.yml: -------------------------------------------------------------------------------- 1 | name: Compile Examples 2 | 3 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | paths: 7 | - ".github/workflows/compile-examples.yml" 8 | - "examples/**" 9 | - "**.c" 10 | - "**.cpp" 11 | - "**.h" 12 | - "*.S" 13 | pull_request: 14 | paths: 15 | - ".github/workflows/compile-examples.yml" 16 | - "examples/**" 17 | - "**.c" 18 | - "**.cpp" 19 | - "**.h" 20 | - "*.S" 21 | schedule: 22 | # Run every Tuesday at 8 AM UTC to catch breakage caused by changes to external resources (libraries, platforms). 23 | - cron: "0 8 * * TUE" 24 | workflow_dispatch: 25 | repository_dispatch: 26 | 27 | jobs: 28 | build: 29 | name: ${{ matrix.board.fqbn }} 30 | runs-on: ubuntu-latest 31 | 32 | env: 33 | SKETCHES_REPORTS_PATH: sketches-reports 34 | 35 | strategy: 36 | fail-fast: false 37 | 38 | matrix: 39 | board: 40 | - fqbn: esp8266:esp8266:huzzah 41 | platforms: | 42 | - name: esp8266:esp8266 43 | source-url: https://arduino.esp8266.com/stable/package_esp8266com_index.json 44 | artifact-name-suffix: esp8266-esp8266-huzzah 45 | 46 | steps: 47 | - name: Checkout repository 48 | uses: actions/checkout@v4 49 | 50 | - name: Compile examples 51 | uses: arduino/compile-sketches@v1 52 | with: 53 | github-token: ${{ secrets.GITHUB_TOKEN }} 54 | fqbn: ${{ matrix.board.fqbn }} 55 | platforms: ${{ matrix.board.platforms }} 56 | libraries: | 57 | # Install the library from the local path. 58 | - source-path: ./ 59 | # Additional library dependencies can be listed here. 60 | # See: https://github.com/arduino/compile-sketches#libraries 61 | sketch-paths: | 62 | - examples 63 | enable-deltas-report: true 64 | sketches-report-path: ${{ env.SKETCHES_REPORTS_PATH }} 65 | 66 | - name: Save sketches report as workflow artifact 67 | uses: actions/upload-artifact@v4 68 | with: 69 | if-no-files-found: error 70 | path: ${{ env.SKETCHES_REPORTS_PATH }} 71 | name: sketches-report-${{ matrix.board.artifact-name-suffix }} 72 | -------------------------------------------------------------------------------- /.github/workflows/report-size-deltas.yml: -------------------------------------------------------------------------------- 1 | name: Report Size Deltas 2 | 3 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | paths: 7 | - ".github/workflows/report-size-deltas.yml" 8 | schedule: 9 | # Run at the minimum interval allowed by GitHub Actions. 10 | # Note: GitHub Actions periodically has outages which result in workflow failures. 11 | # In this event, the workflows will start passing again once the service recovers. 12 | - cron: "*/5 * * * *" 13 | workflow_dispatch: 14 | repository_dispatch: 15 | 16 | jobs: 17 | report: 18 | runs-on: ubuntu-latest 19 | steps: 20 | - name: Comment size deltas reports to PRs 21 | uses: arduino/report-size-deltas@v1 22 | with: 23 | # Regex matching the names of the workflow artifacts created by the "Compile Examples" workflow 24 | sketches-reports-source: ^sketches-report-.+ 25 | -------------------------------------------------------------------------------- /.github/workflows/spell-check.yml: -------------------------------------------------------------------------------- 1 | name: Spell Check 2 | 3 | # See: https://docs.github.com/en/free-pro-team@latest/actions/reference/events-that-trigger-workflows 4 | on: 5 | push: 6 | pull_request: 7 | schedule: 8 | # Run every Tuesday at 8 AM UTC to catch new misspelling detections resulting from dictionary updates. 9 | - cron: "0 8 * * TUE" 10 | workflow_dispatch: 11 | repository_dispatch: 12 | 13 | jobs: 14 | spellcheck: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | 21 | - name: Spell check 22 | uses: codespell-project/actions-codespell@master 23 | -------------------------------------------------------------------------------- /.github/workflows/sync-labels.yml: -------------------------------------------------------------------------------- 1 | # Source: https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/sync-labels.md 2 | name: Sync Labels 3 | 4 | # See: https://docs.github.com/en/actions/reference/events-that-trigger-workflows 5 | on: 6 | push: 7 | paths: 8 | - ".github/workflows/sync-labels.ya?ml" 9 | - ".github/label-configuration-files/*.ya?ml" 10 | pull_request: 11 | paths: 12 | - ".github/workflows/sync-labels.ya?ml" 13 | - ".github/label-configuration-files/*.ya?ml" 14 | schedule: 15 | # Run daily at 8 AM UTC to sync with changes to shared label configurations. 16 | - cron: "0 8 * * *" 17 | workflow_dispatch: 18 | repository_dispatch: 19 | 20 | env: 21 | CONFIGURATIONS_FOLDER: .github/label-configuration-files 22 | CONFIGURATIONS_ARTIFACT: label-configuration-files 23 | 24 | jobs: 25 | check: 26 | runs-on: ubuntu-latest 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v4 31 | 32 | - name: Download JSON schema for labels configuration file 33 | id: download-schema 34 | uses: carlosperate/download-file-action@v2 35 | with: 36 | file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/arduino-tooling-gh-label-configuration-schema.json 37 | location: ${{ runner.temp }}/label-configuration-schema 38 | 39 | - name: Install JSON schema validator 40 | run: | 41 | sudo npm install \ 42 | --global \ 43 | ajv-cli \ 44 | ajv-formats 45 | 46 | - name: Validate local labels configuration 47 | run: | 48 | # See: https://github.com/ajv-validator/ajv-cli#readme 49 | ajv validate \ 50 | --all-errors \ 51 | -c ajv-formats \ 52 | -s "${{ steps.download-schema.outputs.file-path }}" \ 53 | -d "${{ env.CONFIGURATIONS_FOLDER }}/*.{yml,yaml}" 54 | 55 | download: 56 | needs: check 57 | runs-on: ubuntu-latest 58 | 59 | strategy: 60 | matrix: 61 | filename: 62 | # Filenames of the shared configurations to apply to the repository in addition to the local configuration. 63 | # https://github.com/arduino/tooling-project-assets/blob/main/workflow-templates/assets/sync-labels 64 | - universal.yml 65 | 66 | steps: 67 | - name: Download 68 | uses: carlosperate/download-file-action@v2 69 | with: 70 | file-url: https://raw.githubusercontent.com/arduino/tooling-project-assets/main/workflow-templates/assets/sync-labels/${{ matrix.filename }} 71 | 72 | - name: Pass configuration files to next job via workflow artifact 73 | uses: actions/upload-artifact@v4 74 | with: 75 | path: | 76 | *.yaml 77 | *.yml 78 | if-no-files-found: error 79 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 80 | 81 | sync: 82 | needs: download 83 | runs-on: ubuntu-latest 84 | 85 | steps: 86 | - name: Set environment variables 87 | run: | 88 | # See: https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-environment-variable 89 | echo "MERGED_CONFIGURATION_PATH=${{ runner.temp }}/labels.yml" >> "$GITHUB_ENV" 90 | 91 | - name: Determine whether to dry run 92 | id: dry-run 93 | if: > 94 | github.event_name == 'pull_request' || 95 | ( 96 | ( 97 | github.event_name == 'push' || 98 | github.event_name == 'workflow_dispatch' 99 | ) && 100 | github.ref != format('refs/heads/{0}', github.event.repository.default_branch) 101 | ) 102 | run: | 103 | # Use of this flag in the github-label-sync command will cause it to only check the validity of the 104 | # configuration. 105 | echo "::set-output name=flag::--dry-run" 106 | 107 | - name: Checkout repository 108 | uses: actions/checkout@v4 109 | 110 | - name: Download configuration files artifact 111 | uses: actions/download-artifact@v4 112 | with: 113 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 114 | path: ${{ env.CONFIGURATIONS_FOLDER }} 115 | 116 | - name: Remove unneeded artifact 117 | uses: geekyeggo/delete-artifact@v5 118 | with: 119 | name: ${{ env.CONFIGURATIONS_ARTIFACT }} 120 | 121 | - name: Merge label configuration files 122 | run: | 123 | # Merge all configuration files 124 | shopt -s extglob 125 | cat "${{ env.CONFIGURATIONS_FOLDER }}"/*.@(yml|yaml) > "${{ env.MERGED_CONFIGURATION_PATH }}" 126 | 127 | - name: Install github-label-sync 128 | run: sudo npm install --global github-label-sync 129 | 130 | - name: Sync labels 131 | env: 132 | GITHUB_ACCESS_TOKEN: ${{ secrets.GITHUB_TOKEN }} 133 | run: | 134 | # See: https://github.com/Financial-Times/github-label-sync 135 | github-label-sync \ 136 | --labels "${{ env.MERGED_CONFIGURATION_PATH }}" \ 137 | ${{ steps.dry-run.outputs.flag }} \ 138 | ${{ github.repository }} 139 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | NTPClient 3.1.0 - 2016.05.31 2 | 3 | * Added functions for changing the timeOffset and updateInterval later. Thanks @SirUli 4 | 5 | NTPClient 3.0.0 - 2016.04.19 6 | 7 | * Constructors now require UDP instance argument, to add support for non-ESP8266 boards 8 | * Added optional begin API to override default local port 9 | * Added end API to close UDP socket 10 | * Changed return type of update and forceUpdate APIs to bool, and return success or failure 11 | * Change return type of getDay, getHours, getMinutes, and getSeconds to int 12 | 13 | Older 14 | 15 | * Changes not recorded 16 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Fabrice Weinberg, Arduino SA 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /NTPClient.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * The MIT License (MIT) 3 | * Copyright (c) 2015 by Fabrice Weinberg 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 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | * SOFTWARE. 20 | */ 21 | 22 | #include "NTPClient.h" 23 | 24 | NTPClient::NTPClient(UDP& udp) { 25 | this->_udp = &udp; 26 | } 27 | 28 | NTPClient::NTPClient(UDP& udp, long timeOffset) { 29 | this->_udp = &udp; 30 | this->_timeOffset = timeOffset; 31 | } 32 | 33 | NTPClient::NTPClient(UDP& udp, const char* poolServerName) { 34 | this->_udp = &udp; 35 | this->_poolServerName = poolServerName; 36 | } 37 | 38 | NTPClient::NTPClient(UDP& udp, IPAddress poolServerIP) { 39 | this->_udp = &udp; 40 | this->_poolServerIP = poolServerIP; 41 | this->_poolServerName = NULL; 42 | } 43 | 44 | NTPClient::NTPClient(UDP& udp, const char* poolServerName, long timeOffset) { 45 | this->_udp = &udp; 46 | this->_timeOffset = timeOffset; 47 | this->_poolServerName = poolServerName; 48 | } 49 | 50 | NTPClient::NTPClient(UDP& udp, IPAddress poolServerIP, long timeOffset){ 51 | this->_udp = &udp; 52 | this->_timeOffset = timeOffset; 53 | this->_poolServerIP = poolServerIP; 54 | this->_poolServerName = NULL; 55 | } 56 | 57 | NTPClient::NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval) { 58 | this->_udp = &udp; 59 | this->_timeOffset = timeOffset; 60 | this->_poolServerName = poolServerName; 61 | this->_updateInterval = updateInterval; 62 | } 63 | 64 | NTPClient::NTPClient(UDP& udp, IPAddress poolServerIP, long timeOffset, unsigned long updateInterval) { 65 | this->_udp = &udp; 66 | this->_timeOffset = timeOffset; 67 | this->_poolServerIP = poolServerIP; 68 | this->_poolServerName = NULL; 69 | this->_updateInterval = updateInterval; 70 | } 71 | 72 | void NTPClient::begin() { 73 | this->begin(NTP_DEFAULT_LOCAL_PORT); 74 | } 75 | 76 | void NTPClient::begin(unsigned int port) { 77 | this->_port = port; 78 | 79 | this->_udp->begin(this->_port); 80 | 81 | this->_udpSetup = true; 82 | } 83 | 84 | bool NTPClient::forceUpdate() { 85 | #ifdef DEBUG_NTPClient 86 | Serial.println("Update from NTP Server"); 87 | #endif 88 | 89 | // flush any existing packets 90 | while(this->_udp->parsePacket() != 0) 91 | this->_udp->flush(); 92 | 93 | this->sendNTPPacket(); 94 | 95 | // Wait till data is there or timeout... 96 | byte timeout = 0; 97 | int cb = 0; 98 | do { 99 | delay ( 10 ); 100 | cb = this->_udp->parsePacket(); 101 | if (timeout > 100) return false; // timeout after 1000 ms 102 | timeout++; 103 | } while (cb == 0); 104 | 105 | this->_lastUpdate = millis() - (10 * (timeout + 1)); // Account for delay in reading the time 106 | 107 | this->_udp->read(this->_packetBuffer, NTP_PACKET_SIZE); 108 | 109 | unsigned long highWord = word(this->_packetBuffer[40], this->_packetBuffer[41]); 110 | unsigned long lowWord = word(this->_packetBuffer[42], this->_packetBuffer[43]); 111 | // combine the four bytes (two words) into a long integer 112 | // this is NTP time (seconds since Jan 1 1900): 113 | unsigned long secsSince1900 = highWord << 16 | lowWord; 114 | 115 | this->_currentEpoc = secsSince1900 - SEVENZYYEARS; 116 | 117 | return true; // return true after successful update 118 | } 119 | 120 | bool NTPClient::update() { 121 | if ((millis() - this->_lastUpdate >= this->_updateInterval) // Update after _updateInterval 122 | || this->_lastUpdate == 0) { // Update if there was no update yet. 123 | if (!this->_udpSetup || this->_port != NTP_DEFAULT_LOCAL_PORT) this->begin(this->_port); // setup the UDP client if needed 124 | return this->forceUpdate(); 125 | } 126 | return false; // return false if update does not occur 127 | } 128 | 129 | bool NTPClient::isTimeSet() const { 130 | return (this->_lastUpdate != 0); // returns true if the time has been set, else false 131 | } 132 | 133 | unsigned long NTPClient::getEpochTime() const { 134 | return this->_timeOffset + // User offset 135 | this->_currentEpoc + // Epoch returned by the NTP server 136 | ((millis() - this->_lastUpdate) / 1000); // Time since last update 137 | } 138 | 139 | int NTPClient::getDay() const { 140 | return (((this->getEpochTime() / 86400L) + 4 ) % 7); //0 is Sunday 141 | } 142 | int NTPClient::getHours() const { 143 | return ((this->getEpochTime() % 86400L) / 3600); 144 | } 145 | int NTPClient::getMinutes() const { 146 | return ((this->getEpochTime() % 3600) / 60); 147 | } 148 | int NTPClient::getSeconds() const { 149 | return (this->getEpochTime() % 60); 150 | } 151 | 152 | String NTPClient::getFormattedTime() const { 153 | unsigned long rawTime = this->getEpochTime(); 154 | unsigned long hours = (rawTime % 86400L) / 3600; 155 | String hoursStr = hours < 10 ? "0" + String(hours) : String(hours); 156 | 157 | unsigned long minutes = (rawTime % 3600) / 60; 158 | String minuteStr = minutes < 10 ? "0" + String(minutes) : String(minutes); 159 | 160 | unsigned long seconds = rawTime % 60; 161 | String secondStr = seconds < 10 ? "0" + String(seconds) : String(seconds); 162 | 163 | return hoursStr + ":" + minuteStr + ":" + secondStr; 164 | } 165 | 166 | void NTPClient::end() { 167 | this->_udp->stop(); 168 | 169 | this->_udpSetup = false; 170 | } 171 | 172 | void NTPClient::setTimeOffset(int timeOffset) { 173 | this->_timeOffset = timeOffset; 174 | } 175 | 176 | void NTPClient::setUpdateInterval(unsigned long updateInterval) { 177 | this->_updateInterval = updateInterval; 178 | } 179 | 180 | void NTPClient::setPoolServerName(const char* poolServerName) { 181 | this->_poolServerName = poolServerName; 182 | } 183 | 184 | void NTPClient::sendNTPPacket() { 185 | // set all bytes in the buffer to 0 186 | memset(this->_packetBuffer, 0, NTP_PACKET_SIZE); 187 | // Initialize values needed to form NTP request 188 | this->_packetBuffer[0] = 0b11100011; // LI, Version, Mode 189 | this->_packetBuffer[1] = 0; // Stratum, or type of clock 190 | this->_packetBuffer[2] = 6; // Polling Interval 191 | this->_packetBuffer[3] = 0xEC; // Peer Clock Precision 192 | // 8 bytes of zero for Root Delay & Root Dispersion 193 | this->_packetBuffer[12] = 49; 194 | this->_packetBuffer[13] = 0x4E; 195 | this->_packetBuffer[14] = 49; 196 | this->_packetBuffer[15] = 52; 197 | 198 | // all NTP fields have been given values, now 199 | // you can send a packet requesting a timestamp: 200 | if (this->_poolServerName) { 201 | this->_udp->beginPacket(this->_poolServerName, 123); 202 | } else { 203 | this->_udp->beginPacket(this->_poolServerIP, 123); 204 | } 205 | this->_udp->write(this->_packetBuffer, NTP_PACKET_SIZE); 206 | this->_udp->endPacket(); 207 | } 208 | 209 | void NTPClient::setRandomPort(unsigned int minValue, unsigned int maxValue) { 210 | randomSeed(analogRead(0)); 211 | this->_port = random(minValue, maxValue); 212 | } 213 | -------------------------------------------------------------------------------- /NTPClient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Arduino.h" 4 | 5 | #include 6 | 7 | #define SEVENZYYEARS 2208988800UL 8 | #define NTP_PACKET_SIZE 48 9 | #define NTP_DEFAULT_LOCAL_PORT 1337 10 | 11 | class NTPClient { 12 | private: 13 | UDP* _udp; 14 | bool _udpSetup = false; 15 | 16 | const char* _poolServerName = "pool.ntp.org"; // Default time server 17 | IPAddress _poolServerIP; 18 | unsigned int _port = NTP_DEFAULT_LOCAL_PORT; 19 | long _timeOffset = 0; 20 | 21 | unsigned long _updateInterval = 60000; // In ms 22 | 23 | unsigned long _currentEpoc = 0; // In s 24 | unsigned long _lastUpdate = 0; // In ms 25 | 26 | byte _packetBuffer[NTP_PACKET_SIZE]; 27 | 28 | void sendNTPPacket(); 29 | 30 | public: 31 | NTPClient(UDP& udp); 32 | NTPClient(UDP& udp, long timeOffset); 33 | NTPClient(UDP& udp, const char* poolServerName); 34 | NTPClient(UDP& udp, const char* poolServerName, long timeOffset); 35 | NTPClient(UDP& udp, const char* poolServerName, long timeOffset, unsigned long updateInterval); 36 | NTPClient(UDP& udp, IPAddress poolServerIP); 37 | NTPClient(UDP& udp, IPAddress poolServerIP, long timeOffset); 38 | NTPClient(UDP& udp, IPAddress poolServerIP, long timeOffset, unsigned long updateInterval); 39 | 40 | /** 41 | * Set time server name 42 | * 43 | * @param poolServerName 44 | */ 45 | void setPoolServerName(const char* poolServerName); 46 | 47 | /** 48 | * Set random local port 49 | */ 50 | void setRandomPort(unsigned int minValue = 49152, unsigned int maxValue = 65535); 51 | 52 | /** 53 | * Starts the underlying UDP client with the default local port 54 | */ 55 | void begin(); 56 | 57 | /** 58 | * Starts the underlying UDP client with the specified local port 59 | */ 60 | void begin(unsigned int port); 61 | 62 | /** 63 | * This should be called in the main loop of your application. By default an update from the NTP Server is only 64 | * made every 60 seconds. This can be configured in the NTPClient constructor. 65 | * 66 | * @return true on success, false on failure 67 | */ 68 | bool update(); 69 | 70 | /** 71 | * This will force the update from the NTP Server. 72 | * 73 | * @return true on success, false on failure 74 | */ 75 | bool forceUpdate(); 76 | 77 | /** 78 | * This allows to check if the NTPClient successfully received a NTP packet and set the time. 79 | * 80 | * @return true if time has been set, else false 81 | */ 82 | bool isTimeSet() const; 83 | 84 | int getDay() const; 85 | int getHours() const; 86 | int getMinutes() const; 87 | int getSeconds() const; 88 | 89 | /** 90 | * Changes the time offset. Useful for changing timezones dynamically 91 | */ 92 | void setTimeOffset(int timeOffset); 93 | 94 | /** 95 | * Set the update interval to another frequency. E.g. useful when the 96 | * timeOffset should not be set in the constructor 97 | */ 98 | void setUpdateInterval(unsigned long updateInterval); 99 | 100 | /** 101 | * @return time formatted like `hh:mm:ss` 102 | */ 103 | String getFormattedTime() const; 104 | 105 | /** 106 | * @return time in seconds since Jan. 1, 1970 107 | */ 108 | unsigned long getEpochTime() const; 109 | 110 | /** 111 | * Stops the underlying UDP client 112 | */ 113 | void end(); 114 | }; 115 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NTPClient 2 | 3 | [![Check Arduino status](https://github.com/arduino-libraries/NTPClient/actions/workflows/check-arduino.yml/badge.svg)](https://github.com/arduino-libraries/NTPClient/actions/workflows/check-arduino.yml) 4 | [![Compile Examples status](https://github.com/arduino-libraries/NTPClient/actions/workflows/compile-examples.yml/badge.svg)](https://github.com/arduino-libraries/NTPClient/actions/workflows/compile-examples.yml) 5 | [![Spell Check status](https://github.com/arduino-libraries/NTPClient/actions/workflows/spell-check.yml/badge.svg)](https://github.com/arduino-libraries/NTPClient/actions/workflows/spell-check.yml) 6 | 7 | Connect to an NTP server, here is how: 8 | 9 | ```cpp 10 | #include 11 | // change next line to use with another board/shield 12 | #include 13 | //#include // for WiFi shield 14 | //#include // for WiFi 101 shield or MKR1000 15 | #include 16 | 17 | const char *ssid = ""; 18 | const char *password = ""; 19 | 20 | WiFiUDP ntpUDP; 21 | 22 | // By default 'pool.ntp.org' is used with 60 seconds update interval and 23 | // no offset 24 | NTPClient timeClient(ntpUDP); 25 | 26 | // You can specify the time server pool and the offset, (in seconds) 27 | // additionally you can specify the update interval (in milliseconds). 28 | // NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000); 29 | 30 | void setup(){ 31 | Serial.begin(115200); 32 | WiFi.begin(ssid, password); 33 | 34 | while ( WiFi.status() != WL_CONNECTED ) { 35 | delay ( 500 ); 36 | Serial.print ( "." ); 37 | } 38 | 39 | timeClient.begin(); 40 | } 41 | 42 | void loop() { 43 | timeClient.update(); 44 | 45 | Serial.println(timeClient.getFormattedTime()); 46 | 47 | delay(1000); 48 | } 49 | ``` 50 | 51 | ## Function documentation 52 | `getEpochTime` returns the Unix epoch, which are the seconds elapsed since 00:00:00 UTC on 1 January 1970 (leap seconds are ignored, every day is treated as having 86400 seconds). **Attention**: If you have set a time offset this time offset will be added to your epoch timestamp. 53 | -------------------------------------------------------------------------------- /examples/Advanced/Advanced.ino: -------------------------------------------------------------------------------- 1 | #include 2 | // change next line to use with another board/shield 3 | #include 4 | //#include // for WiFi shield 5 | //#include // for WiFi 101 shield or MKR1000 6 | #include 7 | 8 | const char *ssid = ""; 9 | const char *password = ""; 10 | 11 | WiFiUDP ntpUDP; 12 | 13 | // You can specify the time server pool and the offset (in seconds, can be 14 | // changed later with setTimeOffset() ). Additionally you can specify the 15 | // update interval (in milliseconds, can be changed using setUpdateInterval() ). 16 | NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000); 17 | 18 | void setup(){ 19 | Serial.begin(115200); 20 | 21 | WiFi.begin(ssid, password); 22 | 23 | while ( WiFi.status() != WL_CONNECTED ) { 24 | delay ( 500 ); 25 | Serial.print ( "." ); 26 | } 27 | 28 | timeClient.begin(); 29 | } 30 | 31 | void loop() { 32 | timeClient.update(); 33 | 34 | Serial.println(timeClient.getFormattedTime()); 35 | 36 | delay(1000); 37 | } 38 | -------------------------------------------------------------------------------- /examples/Basic/Basic.ino: -------------------------------------------------------------------------------- 1 | #include 2 | // change next line to use with another board/shield 3 | #include 4 | //#include // for WiFi shield 5 | //#include // for WiFi 101 shield or MKR1000 6 | #include 7 | 8 | const char *ssid = ""; 9 | const char *password = ""; 10 | 11 | WiFiUDP ntpUDP; 12 | NTPClient timeClient(ntpUDP); 13 | 14 | void setup(){ 15 | Serial.begin(115200); 16 | 17 | WiFi.begin(ssid, password); 18 | 19 | while ( WiFi.status() != WL_CONNECTED ) { 20 | delay ( 500 ); 21 | Serial.print ( "." ); 22 | } 23 | 24 | timeClient.begin(); 25 | } 26 | 27 | void loop() { 28 | timeClient.update(); 29 | 30 | Serial.println(timeClient.getFormattedTime()); 31 | 32 | delay(1000); 33 | } 34 | -------------------------------------------------------------------------------- /examples/IsTimeSet/IsTimeSet.ino: -------------------------------------------------------------------------------- 1 | #include 2 | // change next line to use with another board/shield 3 | #include 4 | //#include // for WiFi shield 5 | //#include // for WiFi 101 shield or MKR1000 6 | #include 7 | 8 | const char *ssid = ""; 9 | const char *password = ""; 10 | 11 | WiFiUDP ntpUDP; 12 | // initialized to a time offset of 10 hours 13 | NTPClient timeClient(ntpUDP,"pool.ntp.org", 36000, 60000); 14 | // HH:MM:SS 15 | // timeClient initializes to 10:00:00 if it does not receive an NTP packet 16 | // before the 100ms timeout. 17 | // without isTimeSet() the LED would be switched on, although the time 18 | // was not yet set correctly. 19 | 20 | // blue LED on ESP-12F 21 | const int led = 2; 22 | const int hour = 10; 23 | const int minute = 0; 24 | 25 | void setup(){ 26 | Serial.begin(115200); 27 | 28 | pinMode(led, OUTPUT); 29 | // led is off when pin is high 30 | digitalWrite(led, 1); 31 | 32 | WiFi.begin(ssid, password); 33 | 34 | while (WiFi.status() != WL_CONNECTED) { 35 | delay (500); 36 | Serial.print ("."); 37 | } 38 | 39 | timeClient.begin(); 40 | } 41 | 42 | void loop() { 43 | timeClient.update(); 44 | 45 | Serial.println(timeClient.getFormattedTime()); 46 | if(timeClient.isTimeSet()) { 47 | if (hour == timeClient.getHours() && minute == timeClient.getMinutes()) { 48 | digitalWrite(led, 0); 49 | } 50 | } 51 | 52 | delay(1000); 53 | } 54 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Datatypes (KEYWORD1) 3 | ####################################### 4 | 5 | NTPClient KEYWORD1 6 | 7 | ####################################### 8 | # Methods and Functions (KEYWORD2) 9 | ####################################### 10 | 11 | begin KEYWORD2 12 | end KEYWORD2 13 | update KEYWORD2 14 | forceUpdate KEYWORD2 15 | isTimeSet KEYWORD2 16 | getDay KEYWORD2 17 | getHours KEYWORD2 18 | getMinutes KEYWORD2 19 | getSeconds KEYWORD2 20 | getFormattedTime KEYWORD2 21 | getEpochTime KEYWORD2 22 | setTimeOffset KEYWORD2 23 | setUpdateInterval KEYWORD2 24 | setPoolServerName KEYWORD2 25 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=NTPClient 2 | version=3.2.1 3 | author=Fabrice Weinberg 4 | maintainer=Arduino 5 | sentence=An NTPClient to connect to a time server 6 | paragraph=Get time from a NTP server and keep it in sync. 7 | category=Timing 8 | url=https://github.com/arduino-libraries/NTPClient 9 | architectures=* 10 | --------------------------------------------------------------------------------