Skip to content

Commit ca447f9

Browse files
committed
Merge pull request #47 from sandeepmistry/multicast-udp
Added Multicast UDP support and MDNS class
2 parents 7b562c6 + 68509e7 commit ca447f9

File tree

6 files changed

+453
-8
lines changed

6 files changed

+453
-8
lines changed
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/*
2+
MDNS WiFi Web Server
3+
4+
A simple web server that shows the value of the analog input pins,
5+
and exposes itself on the MDNS name 'wifi101.local'.
6+
7+
On Linux (like Ubuntu 15.04) or OSX you can access the web page
8+
on the device in a browser at 'http://wifi101.local/'.
9+
10+
On Windows you'll first need to install the Bonjour Printer Services
11+
from:
12+
https://support.apple.com/kb/dl999?locale=en_US
13+
Then you can access the device in a browser at 'http://wifi101.local/'.
14+
15+
This example is written for a network using WPA encryption. For
16+
WEP or WPA, change the Wifi.begin() call accordingly.
17+
18+
Circuit:
19+
* WiFi shield attached
20+
* Analog inputs attached to pins A0 through A5 (optional)
21+
22+
created 13 July 2010
23+
by dlf (Metodo2 srl)
24+
modified 31 May 2012
25+
by Tom Igoe
26+
modified 27 January 2016
27+
by Tony DiCola
28+
29+
*/
30+
31+
#include <SPI.h>
32+
#include <WiFi101.h>
33+
#include <WifiMdns.h>
34+
35+
char ssid[] = "yourNetwork"; // your network SSID (name)
36+
char pass[] = "secretPassword"; // your network password
37+
int keyIndex = 0; // your network key Index number (needed only for WEP)
38+
39+
char mdnsName[] = "wifi101"; // the MDNS name that the board will respond to
40+
// Note that the actual MDNS name will have '.local' after
41+
// the name above, so "wifi101" will be accessible on
42+
// the MDNS name "wifi101.local".
43+
44+
int status = WL_IDLE_STATUS;
45+
46+
// Create a MDNS responder to listen and respond to MDNS name requests.
47+
MDNSResponder mdns;
48+
49+
WiFiServer server(80);
50+
51+
void setup() {
52+
//Initialize serial and wait for port to open:
53+
Serial.begin(9600);
54+
while (!Serial) {
55+
; // wait for serial port to connect. Needed for native USB port only
56+
}
57+
58+
// check for the presence of the shield:
59+
if (WiFi.status() == WL_NO_SHIELD) {
60+
Serial.println("WiFi shield not present");
61+
// don't continue:
62+
while (true);
63+
}
64+
65+
// attempt to connect to Wifi network:
66+
while ( status != WL_CONNECTED) {
67+
Serial.print("Attempting to connect to SSID: ");
68+
Serial.println(ssid);
69+
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
70+
status = WiFi.begin(ssid, pass);
71+
72+
// wait 10 seconds for connection:
73+
delay(10000);
74+
}
75+
// you're connected now, so print out the status:
76+
printWifiStatus();
77+
78+
server.begin();
79+
80+
// Setup the MDNS responder to listen to the configured name.
81+
// NOTE: You _must_ call this _after_ connecting to the WiFi network and
82+
// being assigned an IP address.
83+
if (!mdns.begin(mdnsName)) {
84+
Serial.println("Failed to start MDNS responder!");
85+
while(1);
86+
}
87+
88+
Serial.print("Server listening at http://");
89+
Serial.print(mdnsName);
90+
Serial.println(".local/");
91+
}
92+
93+
94+
void loop() {
95+
// Call the update() function on the MDNS responder every loop iteration to
96+
// make sure it can detect and respond to name requests.
97+
mdns.poll();
98+
99+
// listen for incoming clients
100+
WiFiClient client = server.available();
101+
if (client) {
102+
Serial.println("new client");
103+
// an http request ends with a blank line
104+
boolean currentLineIsBlank = true;
105+
while (client.connected()) {
106+
if (client.available()) {
107+
char c = client.read();
108+
Serial.write(c);
109+
// if you've gotten to the end of the line (received a newline
110+
// character) and the line is blank, the http request has ended,
111+
// so you can send a reply
112+
if (c == '\n' && currentLineIsBlank) {
113+
// send a standard http response header
114+
client.println("HTTP/1.1 200 OK");
115+
client.println("Content-Type: text/html");
116+
client.println("Connection: close"); // the connection will be closed after completion of the response
117+
client.println("Refresh: 5"); // refresh the page automatically every 5 sec
118+
client.println();
119+
client.println("<!DOCTYPE HTML>");
120+
client.println("<html>");
121+
// output the value of each analog input pin
122+
for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
123+
int sensorReading = analogRead(analogChannel);
124+
client.print("analog input ");
125+
client.print(analogChannel);
126+
client.print(" is ");
127+
client.print(sensorReading);
128+
client.println("<br />");
129+
}
130+
client.println("</html>");
131+
break;
132+
}
133+
if (c == '\n') {
134+
// you're starting a new line
135+
currentLineIsBlank = true;
136+
}
137+
else if (c != '\r') {
138+
// you've gotten a character on the current line
139+
currentLineIsBlank = false;
140+
}
141+
}
142+
}
143+
// give the web browser time to receive the data
144+
delay(1);
145+
146+
// close the connection:
147+
client.stop();
148+
Serial.println("client disconnected");
149+
}
150+
}
151+
152+
153+
void printWifiStatus() {
154+
// print the SSID of the network you're attached to:
155+
Serial.print("SSID: ");
156+
Serial.println(WiFi.SSID());
157+
158+
// print your WiFi shield's IP address:
159+
IPAddress ip = WiFi.localIP();
160+
Serial.print("IP Address: ");
161+
Serial.println(ip);
162+
163+
// print the received signal strength:
164+
long rssi = WiFi.RSSI();
165+
Serial.print("signal strength (RSSI):");
166+
Serial.print(rssi);
167+
Serial.println(" dBm");
168+
}

keywords.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ flush KEYWORD2
2525
stop KEYWORD2
2626
connected KEYWORD2
2727
begin KEYWORD2
28+
beginMulti KEYWORD2
2829
disconnect KEYWORD2
2930
macAddress KEYWORD2
3031
localIP KEYWORD2
@@ -36,9 +37,11 @@ RSSI KEYWORD2
3637
encryptionType KEYWORD2
3738
getResult KEYWORD2
3839
getSocket KEYWORD2
40+
poll KEYWORD2
3941
WiFiClient KEYWORD2
4042
WiFiServer KEYWORD2
4143
WiFiSSLClient KEYWORD2
44+
WifiMdns KEYWORD2
4245

4346
#######################################
4447
# Constants (LITERAL1)

src/WiFiMdns.cpp

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
// Port of CC3000 MDNS Responder to WINC1500.
2+
// Author: Tony DiCola
3+
//
4+
// This MDNSResponder class implements just enough MDNS functionality to respond
5+
// to name requests, for example 'foo.local'. This does not implement any other
6+
// MDNS or Bonjour functionality like services, etc.
7+
//
8+
// Copyright (c) 2016 Adafruit Industries. All right reserved.
9+
//
10+
// This library is free software; you can redistribute it and/or
11+
// modify it under the terms of the GNU Lesser General Public
12+
// License as published by the Free Software Foundation; either
13+
// version 2.1 of the License, or (at your option) any later version.
14+
//
15+
// This library is distributed in the hope that it will be useful,
16+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18+
// Lesser General Public License for more details.
19+
//
20+
// You should have received a copy of the GNU Lesser General Public
21+
// License along with this library; if not, write to the Free Software
22+
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23+
24+
#include <avr/pgmspace.h>
25+
26+
#include "Arduino.h"
27+
#include "WiFiMdns.h"
28+
29+
// Important RFC's for reference:
30+
// - DNS request and response: http://www.ietf.org/rfc/rfc1035.txt
31+
// - Multicast DNS: http://www.ietf.org/rfc/rfc6762.txt
32+
33+
#define HEADER_SIZE 12
34+
#define TTL_OFFSET 4
35+
#define IP_OFFSET 10
36+
37+
const uint8_t expectedRequestHeader[HEADER_SIZE] PROGMEM = {
38+
0x00, 0x00,
39+
0x00, 0x00,
40+
0x00, 0x01,
41+
0x00, 0x00,
42+
0x00, 0x00,
43+
0x00, 0x00
44+
};
45+
46+
const uint8_t responseHeader[] PROGMEM = {
47+
0x00, 0x00, // ID = 0
48+
0x84, 0x00, // Flags = response + authoritative answer
49+
0x00, 0x00, // Question count = 0
50+
0x00, 0x01, // Answer count = 1
51+
0x00, 0x00, // Name server records = 0
52+
0x00, 0x01 // Additional records = 1
53+
};
54+
55+
// Generate positive response for IPV4 address
56+
const uint8_t aRecord[] PROGMEM = {
57+
0x00, 0x01, // Type = 1, A record/IPV4 address
58+
0x80, 0x01, // Class = Internet, with cache flush bit
59+
0x00, 0x00, 0x00, 0x00, // TTL in seconds, to be filled in later
60+
0x00, 0x04, // Length of record
61+
0x00, 0x00, 0x00, 0x00 // IP address, to be filled in later
62+
};
63+
64+
// Generate negative response for IPV6 address (CC3000 doesn't support IPV6)
65+
const uint8_t nsecRecord[] PROGMEM = {
66+
0xC0, 0x0C, // Name offset
67+
0x00, 0x2F, // Type = 47, NSEC (overloaded by MDNS)
68+
0x80, 0x01, // Class = Internet, with cache flush bit
69+
0x00, 0x00, 0x00, 0x00, // TTL in seconds, to be filled in later
70+
0x00, 0x08, // Length of record
71+
0xC0, 0x0C, // Next domain = offset to FQDN
72+
0x00, // Block number = 0
73+
0x04, // Length of bitmap = 4 bytes
74+
0x40, 0x00, 0x00, 0x00 // Bitmap value = Only first bit (A record/IPV4) is set
75+
};
76+
77+
const uint8_t domain[] PROGMEM = { 'l', 'o', 'c', 'a', 'l' };
78+
79+
MDNSResponder::MDNSResponder() :
80+
expectedRequestLength(0)
81+
{
82+
}
83+
84+
MDNSResponder::~MDNSResponder()
85+
{
86+
}
87+
88+
bool MDNSResponder::begin(const char* _name, uint32_t _ttlSeconds)
89+
{
90+
int nameLength = strlen(_name);
91+
92+
if (nameLength > 255) {
93+
// Can only handle domains that are upto 255 chars in length.
94+
expectedRequestLength = 0;
95+
return false;
96+
}
97+
98+
name = _name;
99+
ttlSeconds = _ttlSeconds;
100+
101+
name.toLowerCase();
102+
expectedRequestLength = HEADER_SIZE + 1 + nameLength + 1 + sizeof(domain) + 5;
103+
104+
// Open the MDNS UDP listening socket on port 5353 with multicast address
105+
// 224.0.0.251 (0xE00000FB)
106+
if (!udpSocket.beginMulti(IPAddress(224, 0, 0, 251), 5353)) {
107+
return false;
108+
}
109+
110+
return true;
111+
}
112+
113+
void MDNSResponder::poll()
114+
{
115+
if (parseRequest()) {
116+
replyToRequest();
117+
}
118+
}
119+
120+
bool MDNSResponder::parseRequest()
121+
{
122+
if (udpSocket.parsePacket()) {
123+
// check if parsed packet matches expected request length
124+
if (udpSocket.available() != expectedRequestLength) {
125+
// it does not, read the full packet in and drop data
126+
while(udpSocket.available()) {
127+
udpSocket.read();
128+
}
129+
130+
return false;
131+
}
132+
133+
// read packet
134+
uint8_t request[expectedRequestLength];
135+
udpSocket.read(request, expectedRequestLength);
136+
137+
// parse request
138+
uint8_t requestNameLength = request[HEADER_SIZE];
139+
uint8_t* requestName = &request[HEADER_SIZE + 1];
140+
uint8_t requestDomainLength = request[HEADER_SIZE + 1 + requestNameLength];
141+
uint8_t* requestDomain = &request[HEADER_SIZE + 1 + requestNameLength + 1];
142+
uint16_t requestQtype;
143+
uint16_t requestQclass;
144+
145+
memcpy(&requestQtype, &request[expectedRequestLength - 4], sizeof(requestQtype));
146+
memcpy(&requestQclass, &request[expectedRequestLength - 2], sizeof(requestQclass));
147+
148+
requestQtype = _ntohs(requestQtype);
149+
requestQclass = _ntohs(requestQclass);
150+
151+
// compare request
152+
if (memcmp_P(request, expectedRequestHeader, HEADER_SIZE) == 0 && // request header match
153+
requestNameLength == name.length() && // name length match
154+
strncasecmp(name.c_str(), (char*)requestName, requestNameLength) == 0 && // name match
155+
requestDomainLength == sizeof(domain) && // domain length match
156+
memcmp_P(requestDomain, domain, requestDomainLength) == 0 && // suffix match
157+
requestQtype == 0x0001 && // request QType match
158+
requestQclass == 0x0001) { // request QClass match
159+
160+
return true;
161+
}
162+
}
163+
164+
return false;
165+
}
166+
167+
void MDNSResponder::replyToRequest()
168+
{
169+
int nameLength = name.length();
170+
int domainLength = sizeof(domain);
171+
uint32_t ipAddress = WiFi.localIP();
172+
uint32_t ttl = _htonl(ttlSeconds);
173+
174+
int responseSize = sizeof(responseHeader) + 1 + nameLength + 1 + domainLength + 1 + sizeof(aRecord) + sizeof(nsecRecord);
175+
uint8_t response[responseSize];
176+
uint8_t* r = response;
177+
178+
// copy header
179+
memcpy_P(r, responseHeader, sizeof(responseHeader));
180+
r += sizeof(responseHeader);
181+
182+
// copy name
183+
*r = nameLength;
184+
memcpy(r + 1, name.c_str(), nameLength);
185+
r += (1 + nameLength);
186+
187+
// copy domain
188+
*r = domainLength;
189+
memcpy_P(r + 1, domain, domainLength);
190+
r += (1 + domainLength);
191+
192+
// terminator
193+
*r = 0x00;
194+
r++;
195+
196+
// copy A record
197+
memcpy_P(r, aRecord, sizeof(aRecord));
198+
memcpy(r + TTL_OFFSET, &ttl, sizeof(ttl)); // replace TTL value
199+
memcpy(r + IP_OFFSET, &ipAddress, sizeof(ipAddress)); // replace IP address value
200+
r += sizeof(aRecord);
201+
202+
// copy NSEC record
203+
memcpy_P(r, nsecRecord, sizeof(nsecRecord));
204+
r += sizeof(nsecRecord);
205+
206+
udpSocket.beginPacket(IPAddress(224, 0, 0, 251), 5353);
207+
udpSocket.write(response, responseSize);
208+
udpSocket.endPacket();
209+
}

0 commit comments

Comments
 (0)