-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
implement async DNS lookup - no more stuck NTP requests #5332
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
c180a3f
32cda7d
73ae3de
e78b4a2
144c0f9
ff9067f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| #pragma once | ||
| /* | ||
| asyncDNS.h - wrapper class for asynchronous DNS lookups using lwIP | ||
| by @dedehai | ||
| */ | ||
|
|
||
| #ifndef ASYNC_DNS_H | ||
| #define ASYNC_DNS_H | ||
|
|
||
| #include <Arduino.h> | ||
| #include <lwip/dns.h> | ||
| #include <lwip/err.h> | ||
|
|
||
| enum class DnsResult { Idle, Busy, Success, Error }; | ||
DedeHai marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| class AsyncDNS { | ||
| private: | ||
| ip_addr_t _raw_addr; | ||
| volatile DnsResult _status = DnsResult::Idle; | ||
DedeHai marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| uint16_t _errorcount = 0; | ||
|
|
||
| // callback for dns_gethostbyname(), called when lookup is complete or timed out | ||
| static void _dns_callback(const char *name, const ip_addr_t *ipaddr, void *arg) { | ||
|
||
| AsyncDNS* instance = static_cast<AsyncDNS*>(arg); | ||
DedeHai marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if (ipaddr) { | ||
| instance->_raw_addr = *ipaddr; | ||
| instance->_status = DnsResult::Success; | ||
| } else { | ||
| instance->_status = DnsResult::Error; // note: if query timed out (~5s), DNS lookup is broken until WiFi connection is reset (IDF V4 bug) | ||
| instance->_errorcount++; | ||
| } | ||
| } | ||
|
|
||
| public: | ||
| // note: passing the IP as a pointer to query() is not implemented because it is not thread-safe without mutexes | ||
| // with the current IDF bug external error handlig is required anyway or dns will just stay stuck | ||
DedeHai marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // non-blocking query function to start DNS lookup | ||
| DnsResult query(const char* hostname) { | ||
| if (_status == DnsResult::Busy) return DnsResult::Busy; // in progress, waiting for callback | ||
|
|
||
| _status = DnsResult::Busy; | ||
| err_t err = dns_gethostbyname(hostname, &_raw_addr, _dns_callback, this); | ||
|
|
||
| if (err == ERR_OK) { | ||
| _status = DnsResult::Success; // result already in cache | ||
| } else if (err != ERR_INPROGRESS) { | ||
| _status = DnsResult::Error; | ||
| _errorcount++; | ||
| } | ||
| return _status; | ||
| } | ||
|
|
||
| // get the IP once Success is returned | ||
| IPAddress getIP() { | ||
DedeHai marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if (_status != DnsResult::Success) return IPAddress(0,0,0,0); | ||
| #ifdef ARDUINO_ARCH_ESP32 | ||
| return IPAddress(_raw_addr.u_addr.ip4.addr); | ||
| #else | ||
| return IPAddress(_raw_addr.addr); | ||
| #endif | ||
| } | ||
|
|
||
| void renew() { _status = DnsResult::Idle; } // reset status to allow re-query | ||
| void reset() { _status = DnsResult::Idle; _errorcount = 0; } // reset status and error count | ||
| DnsResult status() { return _status; } | ||
| uint16_t getErrorCount() { return _errorcount; } | ||
| }; | ||
| #endif // ASYNC_DNS_H | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -131,7 +131,7 @@ bool deserializeConfig(JsonObject doc, bool fromFS) { | |
| getStringFromJson(apPass, ap["psk"] , 65); //normally not present due to security | ||
| //int ap_pskl = ap[F("pskl")]; | ||
| CJSON(apChannel, ap[F("chan")]); | ||
| if (apChannel > 13 || apChannel < 1) apChannel = 1; | ||
| if (apChannel > 13 || apChannel < 1) apChannel = 6; // reset to default if invalid | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this seams unrelated to the PR
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is. I should have changed this line in #5115 but was unaware at the time. |
||
| CJSON(apHide, ap[F("hide")]); | ||
| if (apHide > 1) apHide = 1; | ||
| CJSON(apBehavior, ap[F("behav")]); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| #include "src/dependencies/timezone/Timezone.h" | ||
| #include "wled.h" | ||
| #include "fcn_declare.h" | ||
| #include "asyncDNS.h" | ||
|
|
||
| // WARNING: may cause errors in sunset calculations on ESP8266, see #3400 | ||
| // building with `-D WLED_USE_REAL_MATH` will prevent those errors at the expense of flash and RAM | ||
|
|
@@ -180,6 +181,7 @@ void handleTime() { | |
| } | ||
| } | ||
|
|
||
| AsyncDNS* ntpDNSlookup; | ||
|
||
| void handleNetworkTime() | ||
| { | ||
| if (ntpEnabled && ntpConnected && millis() - ntpLastSyncTime > (1000*NTP_SYNC_INTERVAL) && WLED_CONNECTED) | ||
|
|
@@ -189,26 +191,54 @@ void handleNetworkTime() | |
| #ifdef ARDUINO_ARCH_ESP32 // I had problems using udp.flush() on 8266 | ||
| while (ntpUdp.parsePacket() > 0) ntpUdp.flush(); // flush any existing packets | ||
| #endif | ||
| sendNTPPacket(); | ||
| ntpPacketSentTime = millis(); | ||
| if (!ntpServerIP.fromString(ntpServerName)) // check if server is IP or domain | ||
| { | ||
| if (ntpDNSlookup == nullptr) ntpDNSlookup = new AsyncDNS; | ||
| DnsResult res = ntpDNSlookup->status(); | ||
| switch (res) { | ||
| case DnsResult::Idle: | ||
| //DEBUG_PRINTF_P(PSTR("Resolving NTP server name: %s\n"), ntpServerName); | ||
| Serial.printf_P(PSTR("Resolving NTP server name: %s\n"), ntpServerName); | ||
DedeHai marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| ntpDNSlookup->query(ntpServerName); // start dnslookup asynchronously | ||
| return; | ||
|
|
||
| case DnsResult::Busy: | ||
| return; // still in progress | ||
|
|
||
| case DnsResult::Success: | ||
| DEBUG_PRINTF_P(PSTR("NTP IP resolved: %s\n"), ntpServerIP.toString().c_str()); | ||
| Serial.printf_P(PSTR("NTP IP resolved: %s\n"), ntpServerIP.toString().c_str()); | ||
| ntpServerIP = ntpDNSlookup->getIP(); | ||
| sendNTPPacket(); | ||
DedeHai marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| break; | ||
|
|
||
| case DnsResult::Error: | ||
| DEBUG_PRINTLN(F("NTP DNS failed")); | ||
| ntpDNSlookup->renew(); // try a new lookup next time | ||
| if ((toki.getTimeSource() == TOKI_TS_NONE) || ntpDNSlookup->getErrorCount() >= 3) { | ||
| // no time source or after 3 failed attempts (30min), reset network connection as dns is probably stuck (TODO: IDF bug, should be fixed in V5) | ||
| if (!realtimeMode) forceReconnect = true; // do not disturb streaming clients | ||
| delete ntpDNSlookup; | ||
| ntpDNSlookup = nullptr; | ||
| } | ||
| ntpLastSyncTime = toki.getTimeSource() == TOKI_TS_NONE ? NTP_NEVER : millis() - (1000*NTP_SYNC_INTERVAL - 600000); // keep trying if no time source, otherwise pause for 10 minutes | ||
| break; | ||
| } | ||
| } | ||
| else | ||
| sendNTPPacket(); | ||
| } | ||
| if (checkNTPResponse()) | ||
| { | ||
| ntpLastSyncTime = millis(); | ||
| delete ntpDNSlookup; | ||
| ntpDNSlookup = nullptr; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void sendNTPPacket() | ||
| { | ||
| if (!ntpServerIP.fromString(ntpServerName)) //see if server is IP or domain | ||
| { | ||
| #ifdef ESP8266 | ||
| WiFi.hostByName(ntpServerName, ntpServerIP, 750); | ||
| #else | ||
| WiFi.hostByName(ntpServerName, ntpServerIP); | ||
| #endif | ||
| } | ||
|
|
||
| DEBUG_PRINTLN(F("send NTP")); | ||
| byte pbuf[NTP_PACKET_SIZE]; | ||
|
|
@@ -227,6 +257,7 @@ void sendNTPPacket() | |
| ntpUdp.beginPacket(ntpServerIP, 123); //NTP requests are to port 123 | ||
| ntpUdp.write(pbuf, NTP_PACKET_SIZE); | ||
| ntpUdp.endPacket(); | ||
| ntpPacketSentTime = millis(); | ||
| } | ||
|
|
||
| static bool isValidNtpResponse(const byte* ntpPacket) { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.