Skip to content

Commit 7eb6e1f

Browse files
John Doeigrr
authored andcommitted
Add SSDP Library and let Print::printf to handle longer strings
1 parent 27f354d commit 7eb6e1f

File tree

6 files changed

+554
-2
lines changed

6 files changed

+554
-2
lines changed

hardware/esp8266com/esp8266/cores/esp8266/Print.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ size_t ICACHE_FLASH_ATTR Print::write(const uint8_t *buffer, size_t size) {
4747
size_t Print::printf(const char *format, ...) {
4848
va_list arg;
4949
va_start(arg, format);
50-
char temp[256];
51-
size_t len = ets_vsnprintf(temp, 256, format, arg);
50+
char temp[1460];
51+
size_t len = ets_vsnprintf(temp, 1460, format, arg);
5252
len = print(temp);
5353
va_end(arg);
5454
return len;
Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
/*
2+
ESP8266 Simple Service Discovery
3+
Copyright (c) 2015 Hristo Gochkov
4+
5+
Original (Arduino) version by Filippo Sallemi, July 23, 2014.
6+
Can be found at: https://github.com/nomadnt/uSSDP
7+
8+
License (MIT license):
9+
Permission is hereby granted, free of charge, to any person obtaining a copy
10+
of this software and associated documentation files (the "Software"), to deal
11+
in the Software without restriction, including without limitation the rights
12+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
copies of the Software, and to permit persons to whom the Software is
14+
furnished to do so, subject to the following conditions:
15+
16+
The above copyright notice and this permission notice shall be included in
17+
all copies or substantial portions of the Software.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
THE SOFTWARE.
26+
27+
*/
28+
29+
#include "ESP8266SSDP.h"
30+
31+
extern "C" {
32+
#include "ip_addr.h"
33+
#include "user_interface.h"
34+
#include "mem.h"
35+
}
36+
#include "lwip/igmp.h"
37+
38+
#define SSDP_INTERVAL 1200
39+
#define SSDP_PORT 1900
40+
#define SSDP_METHOD_SIZE 10
41+
#define SSDP_URI_SIZE 2
42+
#define SSDP_BUFFER_SIZE 64
43+
44+
static const IPAddress SSDP_MULTICAST_ADDR(239, 255, 255, 250);
45+
46+
const char* _ssdp_response_template =
47+
"HTTP/1.1 200 OK\r\n"
48+
"EXT:\r\n"
49+
"ST: upnp:rootdevice\r\n";
50+
51+
const char* _ssdp_notify_template =
52+
"NOTIFY * HTTP/1.1\r\n"
53+
"HOST: 239.255.255.250:1900\r\n"
54+
"NT: upnp:rootdevice\r\n"
55+
"NTS: ssdp:alive\r\n";
56+
57+
const char* _ssdp_packet_template =
58+
"%s" // _ssdp_response_template / _ssdp_notify_template
59+
"CACHE-CONTROL: max-age=%u\r\n" // SSDP_INTERVAL
60+
"SERVER: Arduino/1.0 UPNP/1.1 %s/%u.%u\r\n" // _modelName, _modelNumber->major, _modelNumber->minor
61+
"USN: uuid:%s-%02X%02X%02X%02X%02X%02X\r\n" // _base, _mac[0], _mac[1], _mac[2], _mac[3], _mac[4], _mac[5]
62+
"LOCATION: http://%u.%u.%u.%u/ssdp/schema.xml\r\n" // WiFi.localIP()
63+
"\r\n";
64+
65+
const char* _ssdp_schema_template =
66+
"HTTP/1.1 200 OK\r\n"
67+
"Content-Type: text/xml\r\n"
68+
"Connection: close\r\n"
69+
"Access-Control-Allow-Origin: *\r\n"
70+
"\r\n"
71+
"<?xml version=\"1.0\"?>"
72+
"<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
73+
"<specVersion>"
74+
"<major>1</major>"
75+
"<minor>0</minor>"
76+
"</specVersion>"
77+
"<device>"
78+
"<deviceType>urn:schemas-upnp-org:device:Basic:1</deviceType>"
79+
"<friendlyName>%s</friendlyName>"
80+
"<presentationURL>%s</presentationURL>"
81+
"<serialNumber>%s</serialNumber>"
82+
"<modelName>%s</modelName>"
83+
"<modelNumber>%u.%u</modelNumber>"
84+
"<modelURL>%s</modelURL>"
85+
"<manufacturer>%s</manufacturer>"
86+
"<manufacturerURL>%s</manufacturerURL>"
87+
"<UDN>%s-%02X%02X%02X%02X%02X%02X</UDN>" //uuid:UUID
88+
"</device>"
89+
"</root>\r\n"
90+
"\r\n";
91+
92+
SSDPClass::SSDPClass(){
93+
_base = (char*)os_malloc(SSDP_BASE_SIZE);
94+
_mac = (byte*)os_malloc(6);
95+
_friendlyName = (char*)os_malloc(SSDP_FRIENDLY_NAME_SIZE);
96+
_presentationURL = (char*)os_malloc(SSDP_PRESENTATION_URL_SIZE);
97+
_serialNumber = (char*)os_malloc(SSDP_SERIAL_NUMBER_SIZE);
98+
_modelName = (char*)os_malloc(SSDP_MODEL_NAME_SIZE);
99+
_modelNumber = (ssdp_version_t*)os_malloc(SSDP_MODEL_VERSION_SIZE);
100+
_modelURL = (char*)os_malloc(SSDP_MODEL_URL_SIZE);
101+
_manufacturer = (char*)os_malloc(SSDP_MANUFACTURER_SIZE);
102+
_manufacturerURL = (char*)os_malloc(SSDP_MANUFACTURER_URL_SIZE);
103+
104+
_modelNumber->major = 0;
105+
_modelNumber->minor = 0;
106+
_friendlyName[0] = '\0';
107+
_presentationURL[0] = '\0';
108+
_serialNumber[0] = '\0';
109+
_modelName[0] = '\0';
110+
_modelURL[0] = '\0';
111+
_manufacturer[0] = '\0';
112+
_manufacturerURL[0] = '\0';
113+
_pending = false;
114+
}
115+
116+
SSDPClass::~SSDPClass(){
117+
os_free(_base);
118+
os_free(_mac);
119+
os_free(_friendlyName);
120+
os_free(_presentationURL);
121+
os_free(_serialNumber);
122+
os_free(_modelName);
123+
os_free(_modelNumber);
124+
os_free(_modelURL);
125+
os_free(_manufacturer);
126+
os_free(_manufacturerURL);
127+
_pending = false;
128+
}
129+
130+
void SSDPClass::begin(char *base){
131+
ip_addr_t ifaddr;
132+
ip_addr_t multicast_addr;
133+
134+
_pending = false;
135+
strcpy(_base, base);
136+
137+
WiFi.macAddress(_mac);
138+
ifaddr.addr = WiFi.localIP();
139+
multicast_addr.addr = (uint32_t) SSDP_MULTICAST_ADDR;
140+
igmp_joingroup(&ifaddr, &multicast_addr);
141+
142+
_server.begin(SSDP_PORT);
143+
}
144+
145+
void SSDPClass::send(ssdp_method_t method){
146+
#ifdef DEBUG_SSDP
147+
if(method == NONE){
148+
DEBUG_SSDP.print("Sending Response to ");
149+
DEBUG_SSDP.print(_server.remoteIP());
150+
DEBUG_SSDP.print(":");
151+
DEBUG_SSDP.println(_server.remotePort());
152+
}else if(method == NOTIFY){
153+
DEBUG_SSDP.println("Sending Notify to 239.255.255.250:1900");
154+
}
155+
#endif
156+
157+
if(method == NONE){
158+
_server.beginPacket(_server.remoteIP(), _server.remotePort());
159+
} else {
160+
_server.beginPacket(SSDP_MULTICAST_ADDR, SSDP_PORT);
161+
}
162+
163+
uint32_t ip = WiFi.localIP();
164+
165+
_server.printf(_ssdp_packet_template,
166+
(method == NONE)?_ssdp_response_template:_ssdp_notify_template,
167+
SSDP_INTERVAL,
168+
_modelName, _modelNumber->major, _modelNumber->minor,
169+
_base, _mac[0], _mac[1], _mac[2], _mac[3], _mac[4], _mac[5],
170+
(uint8_t)(ip & 0xFF), (uint8_t)((ip >> 8) & 0xFF), (uint8_t)((ip >> 16) & 0xFF), (uint8_t)((ip >> 24) & 0xFF)
171+
);
172+
173+
_server.endPacket();
174+
}
175+
176+
void SSDPClass::schema(WiFiClient client){
177+
client.printf(_ssdp_schema_template,
178+
_friendlyName,
179+
_presentationURL,
180+
_serialNumber,
181+
_modelName,
182+
_modelNumber->major, _modelNumber->minor,
183+
_modelURL,
184+
_manufacturer,
185+
_manufacturerURL,
186+
_base, _mac[0], _mac[1], _mac[2], _mac[3], _mac[4], _mac[5]
187+
);
188+
}
189+
190+
uint8_t SSDPClass::update(){
191+
if(!_pending && _server.parsePacket() > 0){
192+
ssdp_method_t method = NONE;
193+
194+
typedef enum {METHOD, URI, PROTO, KEY, VALUE, ABORT} states;
195+
states state = METHOD;
196+
197+
typedef enum {START, MAN, ST, MX} headers;
198+
headers header = START;
199+
200+
uint8_t cursor = 0;
201+
uint8_t cr = 0;
202+
203+
char buffer[SSDP_BUFFER_SIZE] = {0};
204+
205+
while(_server.available() > 0){
206+
char c = _server.read();
207+
208+
(c == '\r' || c == '\n') ? cr++ : cr = 0;
209+
210+
switch(state){
211+
case METHOD:
212+
if(c == ' '){
213+
if(strcmp(buffer, "M-SEARCH") == 0) method = SEARCH;
214+
else if(strcmp(buffer, "NOTIFY") == 0) method = NOTIFY;
215+
216+
if(method == NONE) state = ABORT;
217+
else state = URI;
218+
cursor = 0;
219+
220+
}else if(cursor < SSDP_METHOD_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; }
221+
break;
222+
case URI:
223+
if(c == ' '){
224+
if(strcmp(buffer, "*")) state = ABORT;
225+
else state = PROTO;
226+
cursor = 0;
227+
}else if(cursor < SSDP_URI_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; }
228+
break;
229+
case PROTO:
230+
if(cr == 2){ state = KEY; cursor = 0; }
231+
break;
232+
case KEY:
233+
if(cr == 4){ _pending = true; _process_time = millis(); }
234+
else if(c == ' '){ cursor = 0; state = VALUE; }
235+
else if(c != '\r' && c != '\n' && c != ':' && cursor < SSDP_BUFFER_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; }
236+
break;
237+
case VALUE:
238+
if(cr == 2){
239+
switch(header){
240+
case MAN:
241+
#ifdef DEBUG_SSDP
242+
DEBUG_SSDP.printf("MAN: %s\n", (char *)buffer);
243+
#endif
244+
break;
245+
case ST:
246+
if(strcmp(buffer, "ssdp:all")){
247+
state = ABORT;
248+
#ifdef DEBUG_SSDP
249+
DEBUG_SSDP.printf("REJECT: %s\n", (char *)buffer);
250+
#endif
251+
}
252+
break;
253+
case MX:
254+
_delay = random(0, atoi(buffer)) * 1000L;
255+
break;
256+
}
257+
258+
if(state != ABORT){ state = KEY; header = START; cursor = 0; }
259+
}else if(c != '\r' && c != '\n'){
260+
if(header == START){
261+
if(strncmp(buffer, "MA", 2) == 0) header = MAN;
262+
else if(strcmp(buffer, "ST") == 0) header = ST;
263+
else if(strcmp(buffer, "MX") == 0) header = MX;
264+
}
265+
266+
if(cursor < SSDP_BUFFER_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; }
267+
}
268+
break;
269+
case ABORT:
270+
_pending = false; _delay = 0;
271+
break;
272+
}
273+
}
274+
275+
_server.flush();
276+
}
277+
278+
if(_pending && (millis() - _process_time) > _delay){
279+
_pending = false; _delay = 0;
280+
send(NONE);
281+
}else if(_notify_time == 0 || (millis() - _notify_time) > (SSDP_INTERVAL * 1000L)){
282+
_notify_time = millis();
283+
send(NOTIFY);
284+
}
285+
}
286+
287+
void SSDPClass::setName(char *name){
288+
strcpy(_friendlyName, name);
289+
}
290+
291+
void SSDPClass::setURL(char *url){
292+
strcpy(_presentationURL, url);
293+
}
294+
295+
void SSDPClass::setSerialNumber(char *serialNumber){
296+
strcpy(_serialNumber, serialNumber);
297+
}
298+
299+
void SSDPClass::setModelName(char *name){
300+
strcpy(_modelName, name);
301+
}
302+
303+
void SSDPClass::setModelNumber(uint8_t major, uint8_t minor){
304+
_modelNumber->major = major;
305+
_modelNumber->minor = minor;
306+
}
307+
308+
void SSDPClass::setModelURL(char *url){
309+
strcpy(_modelURL, url);
310+
}
311+
312+
void SSDPClass::setManufacturer(char *name){
313+
strcpy(_manufacturer, name);
314+
}
315+
316+
void SSDPClass::setManufacturerURL(char *url){
317+
strcpy(_manufacturerURL, url);
318+
}
319+
320+
SSDPClass SSDP;

0 commit comments

Comments
 (0)