{"id":27627073,"url":"https://github.com/mitchsf/ntp2","last_synced_at":"2026-04-25T21:33:42.143Z","repository":{"id":288890524,"uuid":"969468763","full_name":"mitchsf/NTP2","owner":"mitchsf","description":"Non-blocking NTP client for Arduino with Kiss-o'-Death handling.","archived":false,"fork":false,"pushed_at":"2025-04-20T08:37:26.000Z","size":55,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-20T09:27:11.415Z","etag":null,"topics":["arduino","esp32","ntp"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mitchsf.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-04-20T08:04:52.000Z","updated_at":"2025-04-20T08:37:30.000Z","dependencies_parsed_at":"2025-04-20T09:27:13.720Z","dependency_job_id":"f46401a5-6fc2-4c05-aa9e-793698df18c0","html_url":"https://github.com/mitchsf/NTP2","commit_stats":null,"previous_names":["mitchsf/ntp2"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitchsf%2FNTP2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitchsf%2FNTP2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitchsf%2FNTP2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mitchsf%2FNTP2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mitchsf","download_url":"https://codeload.github.com/mitchsf/NTP2/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250443997,"owners_count":21431566,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["arduino","esp32","ntp"],"created_at":"2025-04-23T13:52:39.000Z","updated_at":"2026-04-25T21:33:42.138Z","avatar_url":"https://github.com/mitchsf.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# NTP2 - Non-Blocking NTP Client for Arduino\n\nA lightweight, non-blocking NTP (Network Time Protocol) client library for Arduino with full RFC 5905 Kiss-o'-Death (KoD) support.\n\n## Features\n\n- **Non-blocking operation** — doesn't freeze your sketch while waiting for responses\n- **RFC 5905 compliant** — handles all standard Kiss-o'-Death codes (untested)\n- **Request/response correlation** — stamps each request with a token in the Transmit Timestamp field; rejects responses whose Originate Timestamp doesn't match, preventing acceptance of stale or unrelated packets\n- **Stale packet flushing** — reads all queued UDP packets and keeps only the last valid one, handling servers that send extension fields or duplicate responses\n- **Configurable intervals** — set custom poll, retry, and response timeouts\n- **Millisecond precision** — stores fractional seconds from the NTP Transmit Timestamp for sub-second accuracy (untested)\n- **Automatic error recovery** — backs off to retry interval on errors; restores default interval on next success\n- **Strict time validity** — `badRead()` invalidates cached time so `epoch()` returns 0 until the next successful sync; retries automatically at the retry interval\n- **Plausibility guard** — `epoch()` rejects obviously wrong timestamps outside the 2000–2100 range\n- **Stratum validation** — verifies server quality and synchronization status\n- **Overflow-safe** — handles `millis()` wraparound and NTP timestamp calculations correctly through 2106\n\n## Installation\n\nCopy `NTP2.h` and `NTP2.cpp` to your Arduino libraries folder or include them directly in your sketch directory.\n\n## Basic Usage\n\n```cpp\n#include \u003cWiFiUdp.h\u003e\n#include \"NTP2.h\"\n\nWiFiUdp udp;\nNTP2 ntp(udp);\n\nvoid setup() {\n  Serial.begin(115200);\n  WiFi.begin(\"ssid\", \"password\");\n  \n  while (WiFi.status() != WL_CONNECTED) {\n    delay(500);\n  }\n  \n  ntp.begin();  // Uses pool.ntp.org by default\n}\n\nvoid loop() {\n  NTPStatus status = ntp.update();\n  \n  if (status == NTP_CONNECTED) {\n    Serial.print(\"Unix time: \");\n    Serial.println(ntp.epoch());\n  }\n  \n  // Your other code here - update() is non-blocking\n}\n```\n\n## Advanced Configuration\n\n```cpp\n// Use specific NTP server\nntp.begin(\"time.nist.gov\");\n\n// Or use IP address\nntp.begin(IPAddress(132, 163, 96, 1));\n\n// Configure polling interval (default: 1 hour)\nntp.updateInterval(3600000);  // milliseconds\n\n// Set response timeout (default: 250ms)\nntp.responseDelay(500);\n\n// Set retry delay for errors/KoD (default: 30 seconds)\nntp.retryDelay(60000);\n\n// Force immediate update\nntp.forceUpdate();\n```\n\n## Return Status Codes\n\nThe `update()` method returns one of these status codes:\n\n| Status | Description |\n|--------|-------------|\n| `NTP_IDLE` | Waiting for next poll or response |\n| `NTP_CONNECTED` | Successfully synchronized |\n| `NTP_BAD_PACKET` | Invalid or missing response |\n| `NTP_KOD_RATE` | Rate limiting (too many requests) |\n| `NTP_KOD_DENY` | Access denied |\n| `NTP_KOD_ACST` | Association belongs to anycast server |\n| `NTP_KOD_AUTH` | Authentication failed |\n| `NTP_KOD_AUTO` | Autokey sequence failed |\n| `NTP_KOD_BCST` | Broadcast mode |\n| `NTP_KOD_CRYP` | Cryptographic authentication failed |\n| `NTP_KOD_DROP` | Server dropping packets |\n| `NTP_KOD_RSTR` | Access denied (rate restriction) |\n| `NTP_KOD_INIT` | Association not yet synchronized |\n| `NTP_KOD_MCST` | Multicast mode |\n| `NTP_KOD_NKEY` | No key found |\n| `NTP_KOD_NTSN` | Network Time Security negative-acknowledgment |\n| `NTP_KOD_RMOT` | Somebody else's server |\n| `NTP_KOD_STEP` | Step change in system time |\n| `NTP_UNKNOWN_KOD` | Unrecognized Kiss-o'-Death code |\n\n## API Reference\n\n### Methods\n\n- `void begin()` — Initialize with default server (pool.ntp.org)\n- `void begin(const char* server)` — Initialize with hostname\n- `void begin(IPAddress serverIP)` — Initialize with IP address\n- `void stop()` — Stop NTP client and release UDP port\n- `NTPStatus update()` — Non-blocking update, call frequently in loop()\n- `NTPStatus forceUpdate()` — Force immediate sync request\n- `time_t epoch()` — Get current Unix timestamp (returns 0 if no valid sync)\n- `uint32_t timestamp()` — Get millis() value at last successful sync\n- `bool ntpStat()` — Returns true if last sync succeeded\n- `void updateInterval(unsigned long ms)` — Set polling interval\n- `void responseDelay(uint32_t ms)` — Set response timeout\n- `void retryDelay(uint32_t ms)` — Set error retry delay\n\n### Defaults\n\n| Parameter | Default | Define |\n|-----------|---------|--------|\n| Server | `pool.ntp.org` | `NTP_SERVER` |\n| Poll interval | 3,600,000 ms (1 hr) | `NTP_POLL_INTERVAL` |\n| Response timeout | 250 ms | `NTP_RESPONSE_DELAY` |\n| Retry delay | 30,000 ms (30 s) | `NTP_RETRY_DELAY` |\n\n## How It Works\n\nNTP2 uses a non-blocking state machine:\n\n1. When the poll interval expires (or `forceUpdate()` is called), sends an NTP request with a correlation token in the Transmit Timestamp field.\n2. Returns `NTP_IDLE` while waiting for the response timeout to elapse.\n3. After the timeout, reads all queued UDP packets, keeping the last one of valid size (≥ 48 bytes). This flushes stale packets and discards extension fields.\n4. Validates the response: checks that the Originate Timestamp matches the request token, then verifies protocol fields and extracts the Transmit Timestamp.\n5. On success, stores the NTP time with fractional-second precision, resets the poll interval, and returns `NTP_CONNECTED`.\n6. On failure, invalidates cached time so `epoch()` returns 0, switches to the retry interval, and keeps retrying until a successful sync.\n7. Handles Kiss-o'-Death packets per RFC 5905, mapping all 15 standard KoD codes to distinct status values.\n\n### Validation checks\n\n- Originate Timestamp matches request token (correlation)\n- NTP version 3 or 4\n- Mode 4 (server) or 5 (broadcast)\n- Stratum 1–15 (rejects 0/KoD and 16/unsynchronized)\n- Leap Indicator ≠ 3 (alarm/unsynchronized)\n- Transmit Timestamp non-zero\n- Unix epoch within 2000–2100 plausibility window\n\n## Technical Details\n\n- **Protocol**: NTP v3/v4 (RFC 5905)\n- **UDP Port**: 123\n- **Packet Size**: 48 bytes\n- **Precision**: Millisecond (stores fractional seconds — untested)\n- **Time Base**: Unix epoch (January 1, 1970)\n- **Overflow Safe**: Until February 2106\n\n## License\n\nCopyright (c) 2026 Mitch Feig (mitch@feig.com)\n\n## Credits\n\nUpdated and maintained by Mitch Feig with improvements for RFC compliance, race condition fixes, and enhanced validation.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmitchsf%2Fntp2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmitchsf%2Fntp2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmitchsf%2Fntp2/lists"}