mDNS and NTP
Two small problems that always arrive together - what to call the board when its IP address keeps changing, and what time it thinks it is, given that it starts at midnight on 1 January 1970.
How wrong the clock gets
configTime() already installs a periodic update, so the fix is usually to stop disabling it. On a battery device that deep-sleeps, sync once a day — one NTP exchange is a few hundred bytes.mDNS in one paragraph
The board shouts its name onto the local network and answers when somebody asks
for it. No server, no configuration, no DNS entry. MDNS.begin("greenhouse")
makes it greenhouse.local for anything on the same network that speaks mDNS —
which is macOS, iOS, Windows 10 and later, and Linux with Avahi. Android is the
gap.
Time on a sleeping device
Deep sleep stops the CPU but the RTC keeps counting, so the time survives. A power cut does not. Sync once at boot and once a day; one NTP exchange is a few hundred bytes and a few tens of milliseconds of radio, which is nothing against the wake-up it rides along with.
The code
Four lines for a name, two for a clock. configTime also installs a periodic re-sync, which is the part that keeps a long-running device honest.
#include <WiFi.h>
#include <ESPmDNS.h>
#include <time.h>
void setup() {
Serial.begin(115200);
WiFi.begin("your-network", "your-password");
while (WiFi.status() != WL_CONNECTED) delay(250);
if (MDNS.begin("greenhouse")) { // now greenhouse.local
MDNS.addService("http", "tcp", 80);
}
// Europe/London, with daylight saving handled by the rule
configTzTime("GMT0BST,M3.5.0/1,M10.5.0", "pool.ntp.org", "time.nist.gov");
struct tm t;
if (getLocalTime(&t, 10000)) {
Serial.println(&t, "%Y-%m-%d %H:%M:%S");
} else {
Serial.println("no time yet - carrying on anyway");
}
}
void loop() {}getLocalTime blocks until the first sync arrives or the timeout expires. Give it a real timeout - a board with no internet must still run.
ntptime sets the hardware clock in UTC. Time zones are yours to add, which is honest - the board has no idea where it is.
import network, ntptime, time
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect('your-network', 'your-password')
while not wlan.isconnected():
pass
try:
ntptime.settime() # sets the RTC in UTC
except OSError:
print('no NTP - clock is wrong, carrying on')
print(time.localtime())The RTC keeps running through a soft reset but not through a power cut. On a device that loses power, re-sync at every boot or fit a DS3231.
When it does not work
Android's mDNS support has been patchy for years. Keep the IP address as a fallback, and print it on a display or over serial.
The internal RC oscillator drifts by thousands of parts per million. Re-sync periodically, or fit a crystal-backed RTC module for anything that must keep time offline.
You set an offset instead of a time zone. configTzTime with a POSIX rule handles daylight saving automatically. A fixed offset cannot.
getLocalTime with no timeout, or a blocking NTP call. Give both a timeout and continue - a thermostat that shows the temperature with the wrong clock is far better than one that shows nothing.
The sync lands. Turning that one number into a date, in the right timezone, with daylight saving handled for you.
Keeping time with time.h →Edit this page — content/esp32/mdns-and-ntp.mdx
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.