Skip to content

Commit 58a044b

Browse files
LaborEtArsdevyte
authored andcommitted
LEAmDNS - Multicast DNS Responder (#5442)
* LEAmDNS Responder (II) Created a new branch to solve commit conflicts with recent changes to ESP8266mDNSResponder.cpp by d-a-v. * Removed millis() roll-over issues * fix LEA-mDNS * astyle on lea-mdns examples * add compatibility with lwIP-v1 * ditto * use strncasecmp instead of lwip's strnicmp from lwIP-v2 (thanks @devyte) * ditto * fixes * Update mDNS_Clock.ino unindent * Update mDNS_ServiceMonitor.ino unindent * Update LEAmDNS.h Add setInstanceName() forwarder for compat * Disable DEBUG_ESP_MDNS_RESPONDER by default * [Fixed] Debug output line DEBUG_OUTPUT needs to go inside DEBUG_EX_ function otherwise throw error if DEBUG_ESP_MDNS_RESPONDER is not defined
1 parent e043806 commit 58a044b

17 files changed

+9663
-7
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
/*
2+
ESP8266 mDNS responder clock
3+
4+
This example demonstrates two features of the LEA MDNSResponder:
5+
1. The host and service domain negotiation process that ensures
6+
the uniqueness of the finally choosen host and service domain name.
7+
2. The dynamic MDNS service TXT feature
8+
9+
A 'clock' service in announced via the MDNS responder and the current
10+
time is set as a TXT item (eg. 'curtime=Mon Oct 15 19:54:35 2018').
11+
The time value is updated every second!
12+
13+
The ESP is initially announced to clients as 'esp8266.local', if this host domain
14+
is already used in the local network, another host domain is negociated. Keep an
15+
eye to the serial output to learn the final host domain for the clock service.
16+
The service itself is is announced as 'host domain'._espclk._tcp.local.
17+
As the service uses port 80, a very simple HTTP server is installed also to deliver
18+
a small web page containing a greeting and the current time (not updated).
19+
The web server code is taken nearly 1:1 from the 'mDNS_Web_Server.ino' example.
20+
Point your browser to 'host domain'.local to see this web page.
21+
22+
Instructions:
23+
- Update WiFi SSID and password as necessary.
24+
- Flash the sketch to the ESP8266 board
25+
- Install host software:
26+
- For Linux, install Avahi (http://avahi.org/).
27+
- For Windows, install Bonjour (http://www.apple.com/support/bonjour/).
28+
- For Mac OSX and iOS support is built in through Bonjour already.
29+
- Use a MDNS/Bonjour browser like 'Discovery' to find the clock service in your local
30+
network and see the current time updates.
31+
32+
*/
33+
34+
35+
#include <ESP8266WiFi.h>
36+
#include <WiFiClient.h>
37+
#include <time.h>
38+
39+
/*
40+
Include the MDNSResponder (the library needs to be included also)
41+
As LEA MDNSResponder is experimantal in the ESP8266 environment currently, the
42+
legacy MDNSResponder is defaulted in th include file.
43+
There are two ways to access LEA MDNSResponder:
44+
1. Prepend every declaration and call to global declarations or functions with the namespace, like:
45+
'LEAmDNS::MDNSResponder::hMDNSService hMDNSService;'
46+
This way is used in the example. But be careful, if the namespace declaration is missing
47+
somewhere, the call might go to the legacy implementation...
48+
2. Open 'ESP8266mDNS.h' and set LEAmDNS to default.
49+
50+
*/
51+
#include <ESP8266mDNS.h>
52+
#include <LEATimeFlag.h>
53+
54+
/*
55+
Global defines and vars
56+
*/
57+
58+
#define TIMEZONE_OFFSET 1 // CET
59+
#define DST_OFFSET 1 // CEST
60+
#define UPDATE_CYCLE (1 * 1000) // every second
61+
62+
#define SERVICE_PORT 80 // HTTP port
63+
64+
#ifndef STASSID
65+
#define STASSID "your-ssid"
66+
#define STAPSK "your-password"
67+
#endif
68+
69+
const char* ssid = STASSID;
70+
const char* password = STAPSK;
71+
72+
char* pcHostDomain = 0; // Negociated host domain
73+
bool bHostDomainConfirmed = false; // Flags the confirmation of the host domain
74+
LEAmDNS::MDNSResponder::hMDNSService hMDNSService = 0; // The handle of the clock service in the MDNS responder
75+
76+
// TCP server at port 'SERVICE_PORT' will respond to HTTP requests
77+
WiFiServer server(SERVICE_PORT);
78+
79+
80+
/*
81+
getTimeString
82+
*/
83+
const char* getTimeString(void) {
84+
85+
static char acTimeString[32];
86+
time_t now = time(nullptr);
87+
ctime_r(&now, acTimeString);
88+
size_t stLength;
89+
while (((stLength = os_strlen(acTimeString))) &&
90+
('\n' == acTimeString[stLength - 1])) {
91+
acTimeString[stLength - 1] = 0; // Remove trailing line break...
92+
}
93+
return acTimeString;
94+
}
95+
96+
97+
/*
98+
setClock
99+
100+
Set time via NTP
101+
*/
102+
void setClock(void) {
103+
configTime((TIMEZONE_OFFSET * 3600), (DST_OFFSET * 3600), "pool.ntp.org", "time.nist.gov", "time.windows.com");
104+
105+
Serial.print("Waiting for NTP time sync: ");
106+
time_t now = time(nullptr); // Secs since 01.01.1970 (when uninitalized starts with (8 * 3600 = 28800)
107+
while (now < 8 * 3600 * 2) { // Wait for realistic value
108+
delay(500);
109+
Serial.print(".");
110+
now = time(nullptr);
111+
}
112+
Serial.println("");
113+
Serial.printf("Current time: %s\n", getTimeString());
114+
}
115+
116+
117+
/*
118+
setStationHostname
119+
*/
120+
bool setStationHostname(const char* p_pcHostname) {
121+
122+
if (p_pcHostname) {
123+
WiFi.hostname(p_pcHostname);
124+
Serial.printf("setDeviceHostname: Station hostname is set to '%s'\n", p_pcHostname);
125+
}
126+
return true;
127+
}
128+
129+
130+
/*
131+
MDNSDynamicServiceTxtCallback
132+
133+
Add a dynamic MDNS TXT item 'ct' to the clock service.
134+
The callback function is called every time, the TXT items for the clock service
135+
are needed.
136+
This can be triggered by calling MDNS.announce().
137+
138+
*/
139+
bool MDNSDynamicServiceTxtCallback(LEAmDNS::MDNSResponder* p_pMDNSResponder,
140+
const LEAmDNS::MDNSResponder::hMDNSService p_hService,
141+
void* p_pUserdata) {
142+
Serial.println("MDNSDynamicServiceTxtCallback");
143+
(void) p_pUserdata;
144+
145+
if ((p_pMDNSResponder) &&
146+
(hMDNSService == p_hService)) {
147+
Serial.printf("Updating curtime TXT item to: %s\n", getTimeString());
148+
p_pMDNSResponder->addDynamicServiceTxt(p_hService, "curtime", getTimeString());
149+
}
150+
return true;
151+
}
152+
153+
154+
/*
155+
MDNSProbeResultCallback
156+
157+
Probe result callback for the host domain.
158+
If the domain is free, the host domain is set and the clock service is
159+
added.
160+
If the domain is already used, a new name is created and the probing is
161+
restarted via p_pMDNSResponder->setHostname().
162+
163+
*/
164+
bool MDNSProbeResultCallback(LEAmDNS::MDNSResponder* p_pMDNSResponder,
165+
const char* p_pcDomainName,
166+
const LEAmDNS::MDNSResponder::hMDNSService p_hService,
167+
bool p_bProbeResult,
168+
void* p_pUserdata) {
169+
Serial.println("MDNSProbeResultCallback");
170+
(void) p_pUserdata;
171+
172+
if ((p_pMDNSResponder) &&
173+
(0 == p_hService)) { // Called for host domain
174+
Serial.printf("MDNSProbeResultCallback: Host domain '%s.local' is %s\n", p_pcDomainName, (p_bProbeResult ? "free" : "already USED!"));
175+
if (true == p_bProbeResult) {
176+
// Set station hostname
177+
setStationHostname(pcHostDomain);
178+
179+
if (!bHostDomainConfirmed) {
180+
// Hostname free -> setup clock service
181+
bHostDomainConfirmed = true;
182+
183+
if (!hMDNSService) {
184+
// Add a 'clock.tcp' service to port 'SERVICE_PORT', using the host domain as instance domain
185+
hMDNSService = p_pMDNSResponder->addService(0, "espclk", "tcp", SERVICE_PORT);
186+
if (hMDNSService) {
187+
// Add a simple static MDNS service TXT item
188+
p_pMDNSResponder->addServiceTxt(hMDNSService, "port#", SERVICE_PORT);
189+
// Set the callback function for dynamic service TXTs
190+
p_pMDNSResponder->setDynamicServiceTxtCallback(hMDNSService, MDNSDynamicServiceTxtCallback, 0);
191+
}
192+
}
193+
} else {
194+
// Change hostname, use '-' as divider between base name and index
195+
if (LEAmDNS::MDNSResponder::indexDomain(pcHostDomain, "-", 0)) {
196+
p_pMDNSResponder->setHostname(pcHostDomain);
197+
} else {
198+
Serial.println("MDNSProbeResultCallback: FAILED to update hostname!");
199+
}
200+
}
201+
}
202+
}
203+
return true;
204+
}
205+
206+
207+
/*
208+
handleHTTPClient
209+
*/
210+
void handleHTTPClient(WiFiClient& client) {
211+
Serial.println("");
212+
Serial.println("New client");
213+
214+
// Wait for data from client to become available
215+
while (client.connected() && !client.available()) {
216+
delay(1);
217+
}
218+
219+
// Read the first line of HTTP request
220+
String req = client.readStringUntil('\r');
221+
222+
// First line of HTTP request looks like "GET /path HTTP/1.1"
223+
// Retrieve the "/path" part by finding the spaces
224+
int addr_start = req.indexOf(' ');
225+
int addr_end = req.indexOf(' ', addr_start + 1);
226+
if (addr_start == -1 || addr_end == -1) {
227+
Serial.print("Invalid request: ");
228+
Serial.println(req);
229+
return;
230+
}
231+
req = req.substring(addr_start + 1, addr_end);
232+
Serial.print("Request: ");
233+
Serial.println(req);
234+
client.flush();
235+
236+
// Get current time
237+
time_t now = time(nullptr);;
238+
struct tm timeinfo;
239+
gmtime_r(&now, &timeinfo);
240+
241+
String s;
242+
if (req == "/") {
243+
IPAddress ip = WiFi.localIP();
244+
String ipStr = String(ip[0]) + '.' + String(ip[1]) + '.' + String(ip[2]) + '.' + String(ip[3]);
245+
s = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<!DOCTYPE HTML>\r\n<html>Hello from ESP8266 at ";
246+
s += ipStr;
247+
// Simple addition of the current time
248+
s += "\r\nCurrent time is: ";
249+
s += getTimeString();
250+
// done :-)
251+
s += "</html>\r\n\r\n";
252+
Serial.println("Sending 200");
253+
} else {
254+
s = "HTTP/1.1 404 Not Found\r\n\r\n";
255+
Serial.println("Sending 404");
256+
}
257+
client.print(s);
258+
259+
Serial.println("Done with client");
260+
}
261+
262+
263+
/*
264+
setup
265+
*/
266+
void setup(void) {
267+
Serial.begin(115200);
268+
269+
// Connect to WiFi network
270+
WiFi.mode(WIFI_STA);
271+
WiFi.begin(ssid, password);
272+
Serial.println("");
273+
274+
// Wait for connection
275+
while (WiFi.status() != WL_CONNECTED) {
276+
delay(500);
277+
Serial.print(".");
278+
}
279+
Serial.println("");
280+
Serial.print("Connected to ");
281+
Serial.println(ssid);
282+
Serial.print("IP address: ");
283+
Serial.println(WiFi.localIP());
284+
285+
// Sync clock
286+
setClock();
287+
288+
// Setup MDNS responder
289+
LEAmDNS::MDNS.setProbeResultCallback(MDNSProbeResultCallback, 0);
290+
// Init the (currently empty) host domain string with 'esp8266'
291+
if ((!LEAmDNS::MDNSResponder::indexDomain(pcHostDomain, 0, "esp8266")) ||
292+
(!LEAmDNS::MDNS.begin(pcHostDomain))) {
293+
Serial.println("Error setting up MDNS responder!");
294+
while (1) { // STOP
295+
delay(1000);
296+
}
297+
}
298+
Serial.println("MDNS responder started");
299+
300+
// Start TCP (HTTP) server
301+
server.begin();
302+
Serial.println("TCP server started");
303+
}
304+
305+
306+
/*
307+
loop
308+
*/
309+
void loop(void) {
310+
// Check if a client has connected
311+
WiFiClient client = server.available();
312+
if (client) {
313+
handleHTTPClient(client);
314+
}
315+
316+
// Allow MDNS processing
317+
LEAmDNS::MDNS.update();
318+
319+
// Update time (if needed)
320+
//static unsigned long ulNextTimeUpdate = UPDATE_CYCLE;
321+
static clsLEATimeFlag timeFlag(UPDATE_CYCLE);
322+
if (timeFlag.flagged()/*ulNextTimeUpdate < millis()*/) {
323+
324+
if (hMDNSService) {
325+
// Just trigger a new MDNS announcement, this will lead to a call to
326+
// 'MDNSDynamicServiceTxtCallback', which will update the time TXT item
327+
LEAmDNS::MDNS.announce();
328+
}
329+
//ulNextTimeUpdate = (millis() + UPDATE_CYCLE); // Set update 'timer'
330+
timeFlag.restart();
331+
}
332+
}
333+
334+

0 commit comments

Comments
 (0)