Procházet zdrojové kódy

add copy/paste SSDP with constants in progmem. saves 100s of bytes of SRAM. PR open to esp8266/Arduino to fix

Chris Mullins před 8 roky
rodič
revize
87c630463b
2 změnil soubory, kde provedl 569 přidání a 0 odebrání
  1. 441 0
      lib/SSDP/New_ESP8266SSDP.cpp
  2. 128 0
      lib/SSDP/New_ESP8266SSDP.h

+ 441 - 0
lib/SSDP/New_ESP8266SSDP.cpp

@@ -0,0 +1,441 @@
+/*
+ESP8266 Simple Service Discovery
+Copyright (c) 2015 Hristo Gochkov
+
+Original (Arduino) version by Filippo Sallemi, July 23, 2014.
+Can be found at: https://github.com/nomadnt/uSSDP
+
+License (MIT license):
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+
+*/
+#define LWIP_OPEN_SRC
+#include <functional>
+#include "New_ESP8266SSDP.h"
+#include "WiFiUdp.h"
+#include "debug.h"
+
+extern "C" {
+  #include "osapi.h"
+  #include "ets_sys.h"
+  #include "user_interface.h"
+}
+
+#include "lwip/opt.h"
+#include "lwip/udp.h"
+#include "lwip/inet.h"
+#include "lwip/igmp.h"
+#include "lwip/mem.h"
+#include "include/UdpContext.h"
+
+// #define DEBUG_SSDP  Serial
+
+#define SSDP_INTERVAL     1200
+#define SSDP_PORT         1900
+#define SSDP_METHOD_SIZE  10
+#define SSDP_URI_SIZE     2
+#define SSDP_BUFFER_SIZE  64
+#define SSDP_MULTICAST_TTL 2
+static const IPAddress SSDP_MULTICAST_ADDR(239, 255, 255, 250);
+
+
+
+static const char _ssdp_response_template[] PROGMEM =
+  "HTTP/1.1 200 OK\r\n"
+  "EXT:\r\n";
+
+static const char _ssdp_notify_template[] PROGMEM =
+  "NOTIFY * HTTP/1.1\r\n"
+  "HOST: 239.255.255.250:1900\r\n"
+  "NTS: ssdp:alive\r\n";
+
+static const char _ssdp_packet_template[] PROGMEM =
+  "%s" // _ssdp_response_template / _ssdp_notify_template
+  "CACHE-CONTROL: max-age=%u\r\n" // SSDP_INTERVAL
+  "SERVER: Arduino/1.0 UPNP/1.1 %s/%s\r\n" // _modelName, _modelNumber
+  "USN: uuid:%s\r\n" // _uuid
+  "%s: %s\r\n"  // "NT" or "ST", _deviceType
+  "LOCATION: http://%u.%u.%u.%u:%u/%s\r\n" // WiFi.localIP(), _port, _schemaURL
+  "\r\n";
+
+static const char _ssdp_schema_template[] PROGMEM =
+  "HTTP/1.1 200 OK\r\n"
+  "Content-Type: text/xml\r\n"
+  "Connection: close\r\n"
+  "Access-Control-Allow-Origin: *\r\n"
+  "\r\n"
+  "<?xml version=\"1.0\"?>"
+  "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">"
+    "<specVersion>"
+      "<major>1</major>"
+      "<minor>0</minor>"
+    "</specVersion>"
+    "<URLBase>http://%u.%u.%u.%u:%u/</URLBase>" // WiFi.localIP(), _port
+    "<device>"
+      "<deviceType>%s</deviceType>"
+      "<friendlyName>%s</friendlyName>"
+      "<presentationURL>%s</presentationURL>"
+      "<serialNumber>%s</serialNumber>"
+      "<modelName>%s</modelName>"
+      "<modelNumber>%s</modelNumber>"
+      "<modelURL>%s</modelURL>"
+      "<manufacturer>%s</manufacturer>"
+      "<manufacturerURL>%s</manufacturerURL>"
+      "<UDN>uuid:%s</UDN>"
+    "</device>"
+//    "<iconList>"
+//      "<icon>"
+//        "<mimetype>image/png</mimetype>"
+//        "<height>48</height>"
+//        "<width>48</width>"
+//        "<depth>24</depth>"
+//        "<url>icon48.png</url>"
+//      "</icon>"
+//      "<icon>"
+//       "<mimetype>image/png</mimetype>"
+//       "<height>120</height>"
+//       "<width>120</width>"
+//       "<depth>24</depth>"
+//       "<url>icon120.png</url>"
+//      "</icon>"
+//    "</iconList>"
+  "</root>\r\n"
+  "\r\n";
+
+
+struct SSDPTimer {
+  ETSTimer timer;
+};
+
+SSDPClass::SSDPClass() :
+_server(0),
+_timer(new SSDPTimer),
+_port(80),
+_ttl(SSDP_MULTICAST_TTL),
+_respondToPort(0),
+_pending(false),
+_delay(0),
+_process_time(0),
+_notify_time(0)
+{
+  _uuid[0] = '\0';
+  _modelNumber[0] = '\0';
+  sprintf(_deviceType, "urn:schemas-upnp-org:device:Basic:1");
+  _friendlyName[0] = '\0';
+  _presentationURL[0] = '\0';
+  _serialNumber[0] = '\0';
+  _modelName[0] = '\0';
+  _modelURL[0] = '\0';
+  _manufacturer[0] = '\0';
+  _manufacturerURL[0] = '\0';
+  sprintf(_schemaURL, "ssdp/schema.xml");
+}
+
+SSDPClass::~SSDPClass(){
+  delete _timer;
+}
+
+bool SSDPClass::begin(){
+  _pending = false;
+
+  uint32_t chipId = ESP.getChipId();
+  sprintf(_uuid, "38323636-4558-4dda-9188-cda0e6%02x%02x%02x",
+    (uint16_t) ((chipId >> 16) & 0xff),
+    (uint16_t) ((chipId >>  8) & 0xff),
+    (uint16_t)   chipId        & 0xff  );
+
+#ifdef DEBUG_SSDP
+  DEBUG_SSDP.printf("SSDP UUID: %s\n", (char *)_uuid);
+#endif
+
+  if (_server) {
+    _server->unref();
+    _server = 0;
+  }
+
+  _server = new UdpContext;
+  _server->ref();
+
+  ip_addr_t ifaddr;
+  ifaddr.addr = WiFi.localIP();
+  ip_addr_t multicast_addr;
+  multicast_addr.addr = (uint32_t) SSDP_MULTICAST_ADDR;
+  if (igmp_joingroup(&ifaddr, &multicast_addr) != ERR_OK ) {
+    DEBUGV("SSDP failed to join igmp group");
+    return false;
+  }
+
+  if (!_server->listen(*IP_ADDR_ANY, SSDP_PORT)) {
+    return false;
+  }
+
+  _server->setMulticastInterface(ifaddr);
+  _server->setMulticastTTL(_ttl);
+  _server->onRx(std::bind(&SSDPClass::_update, this));
+  if (!_server->connect(multicast_addr, SSDP_PORT)) {
+    return false;
+  }
+
+  _startTimer();
+
+  return true;
+}
+
+void SSDPClass::_send(ssdp_method_t method){
+  char buffer[1460];
+  uint32_t ip = WiFi.localIP();
+
+  char valueBuffer[strlen(_ssdp_notify_template)+1];
+  strcpy_P(valueBuffer, (method == NONE)?_ssdp_response_template:_ssdp_notify_template);
+
+  int len = snprintf_P(buffer, sizeof(buffer),
+    _ssdp_packet_template,
+    valueBuffer,
+    SSDP_INTERVAL,
+    _modelName, _modelNumber,
+    _uuid,
+    (method == NONE)?"ST":"NT",
+    _deviceType,
+    IP2STR(&ip), _port, _schemaURL
+  );
+
+  _server->append(buffer, len);
+
+  ip_addr_t remoteAddr;
+  uint16_t remotePort;
+  if(method == NONE) {
+    remoteAddr.addr = _respondToAddr;
+    remotePort = _respondToPort;
+#ifdef DEBUG_SSDP
+    DEBUG_SSDP.print("Sending Response to ");
+#endif
+  } else {
+    remoteAddr.addr = SSDP_MULTICAST_ADDR;
+    remotePort = SSDP_PORT;
+#ifdef DEBUG_SSDP
+    DEBUG_SSDP.println("Sending Notify to ");
+#endif
+  }
+#ifdef DEBUG_SSDP
+  DEBUG_SSDP.print(IPAddress(remoteAddr.addr));
+  DEBUG_SSDP.print(":");
+  DEBUG_SSDP.println(remotePort);
+#endif
+
+  _server->send(&remoteAddr, remotePort);
+}
+
+void SSDPClass::schema(WiFiClient client){
+  uint32_t ip = WiFi.localIP();
+  char buffer[strlen(_ssdp_schema_template)+1];
+  strcpy_P(buffer, _ssdp_schema_template);
+  client.printf(buffer,
+    IP2STR(&ip), _port,
+    _deviceType,
+    _friendlyName,
+    _presentationURL,
+    _serialNumber,
+    _modelName,
+    _modelNumber,
+    _modelURL,
+    _manufacturer,
+    _manufacturerURL,
+    _uuid
+  );
+}
+
+void SSDPClass::_update(){
+  if(!_pending && _server->next()) {
+    ssdp_method_t method = NONE;
+
+    _respondToAddr = _server->getRemoteAddress();
+    _respondToPort = _server->getRemotePort();
+
+    typedef enum {METHOD, URI, PROTO, KEY, VALUE, ABORT} states;
+    states state = METHOD;
+
+    typedef enum {START, MAN, ST, MX} headers;
+    headers header = START;
+
+    uint8_t cursor = 0;
+    uint8_t cr = 0;
+
+    char buffer[SSDP_BUFFER_SIZE] = {0};
+
+    while(_server->getSize() > 0){
+      char c = _server->read();
+
+      (c == '\r' || c == '\n') ? cr++ : cr = 0;
+
+      switch(state){
+        case METHOD:
+          if(c == ' '){
+            if(strcmp(buffer, "M-SEARCH") == 0) method = SEARCH;
+            else if(strcmp(buffer, "NOTIFY") == 0) method = NOTIFY;
+
+            if(method == NONE) state = ABORT;
+            else state = URI;
+            cursor = 0;
+
+          } else if(cursor < SSDP_METHOD_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; }
+          break;
+        case URI:
+          if(c == ' '){
+            if(strcmp(buffer, "*")) state = ABORT;
+            else state = PROTO;
+            cursor = 0;
+          } else if(cursor < SSDP_URI_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; }
+          break;
+        case PROTO:
+          if(cr == 2){ state = KEY; cursor = 0; }
+          break;
+        case KEY:
+          if(cr == 4){ _pending = true; _process_time = millis(); }
+          else if(c == ' '){ cursor = 0; state = VALUE; }
+          else if(c != '\r' && c != '\n' && c != ':' && cursor < SSDP_BUFFER_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; }
+          break;
+        case VALUE:
+          if(cr == 2){
+            switch(header){
+              case START:
+                break;
+              case MAN:
+#ifdef DEBUG_SSDP
+                DEBUG_SSDP.printf("MAN: %s\n", (char *)buffer);
+#endif
+                break;
+              case ST:
+                if(strcmp(buffer, "ssdp:all")){
+                  state = ABORT;
+#ifdef DEBUG_SSDP
+                  DEBUG_SSDP.printf("REJECT: %s\n", (char *)buffer);
+#endif
+                }
+                // if the search type matches our type, we should respond instead of ABORT
+                if(strcmp(buffer, _deviceType) == 0){
+                  _pending = true;
+                  _process_time = millis();
+                  state = KEY;
+                }
+                break;
+              case MX:
+                _delay = random(0, atoi(buffer)) * 1000L;
+                break;
+            }
+
+            if(state != ABORT){ state = KEY; header = START; cursor = 0; }
+          } else if(c != '\r' && c != '\n'){
+            if(header == START){
+              if(strncmp(buffer, "MA", 2) == 0) header = MAN;
+              else if(strcmp(buffer, "ST") == 0) header = ST;
+              else if(strcmp(buffer, "MX") == 0) header = MX;
+            }
+
+            if(cursor < SSDP_BUFFER_SIZE - 1){ buffer[cursor++] = c; buffer[cursor] = '\0'; }
+          }
+          break;
+        case ABORT:
+          _pending = false; _delay = 0;
+          break;
+      }
+    }
+  }
+
+  if(_pending && (millis() - _process_time) > _delay){
+    _pending = false; _delay = 0;
+    _send(NONE);
+  } else if(_notify_time == 0 || (millis() - _notify_time) > (SSDP_INTERVAL * 1000L)){
+    _notify_time = millis();
+    _send(NOTIFY);
+  }
+
+  if (_pending) {
+    while (_server->next())
+      _server->flush();
+  }
+
+}
+
+void SSDPClass::setSchemaURL(const char *url){
+  strlcpy(_schemaURL, url, sizeof(_schemaURL));
+}
+
+void SSDPClass::setHTTPPort(uint16_t port){
+  _port = port;
+}
+
+void SSDPClass::setDeviceType(const char *deviceType){
+  strlcpy(_deviceType, deviceType, sizeof(_deviceType));
+}
+
+void SSDPClass::setName(const char *name){
+  strlcpy(_friendlyName, name, sizeof(_friendlyName));
+}
+
+void SSDPClass::setURL(const char *url){
+  strlcpy(_presentationURL, url, sizeof(_presentationURL));
+}
+
+void SSDPClass::setSerialNumber(const char *serialNumber){
+  strlcpy(_serialNumber, serialNumber, sizeof(_serialNumber));
+}
+
+void SSDPClass::setSerialNumber(const uint32_t serialNumber){
+  snprintf(_serialNumber, sizeof(uint32_t)*2+1, "%08X", serialNumber);
+}
+
+void SSDPClass::setModelName(const char *name){
+  strlcpy(_modelName, name, sizeof(_modelName));
+}
+
+void SSDPClass::setModelNumber(const char *num){
+  strlcpy(_modelNumber, num, sizeof(_modelNumber));
+}
+
+void SSDPClass::setModelURL(const char *url){
+  strlcpy(_modelURL, url, sizeof(_modelURL));
+}
+
+void SSDPClass::setManufacturer(const char *name){
+  strlcpy(_manufacturer, name, sizeof(_manufacturer));
+}
+
+void SSDPClass::setManufacturerURL(const char *url){
+  strlcpy(_manufacturerURL, url, sizeof(_manufacturerURL));
+}
+
+void SSDPClass::setTTL(const uint8_t ttl){
+  _ttl = ttl;
+}
+
+void SSDPClass::_onTimerStatic(SSDPClass* self) {
+  self->_update();
+}
+
+void SSDPClass::_startTimer() {
+  ETSTimer* tm = &(_timer->timer);
+  const int interval = 1000;
+  os_timer_disarm(tm);
+  os_timer_setfn(tm, reinterpret_cast<ETSTimerFunc*>(&SSDPClass::_onTimerStatic), reinterpret_cast<void*>(this));
+  os_timer_arm(tm, interval, 1 /* repeat */);
+}
+
+#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SSDP)
+SSDPClass SSDP;
+#endif

+ 128 - 0
lib/SSDP/New_ESP8266SSDP.h

@@ -0,0 +1,128 @@
+/*
+ESP8266 Simple Service Discovery
+Copyright (c) 2015 Hristo Gochkov
+
+Original (Arduino) version by Filippo Sallemi, July 23, 2014.
+Can be found at: https://github.com/nomadnt/uSSDP
+
+License (MIT license):
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+
+*/
+
+#ifndef ESP8266SSDP_H
+#define ESP8266SSDP_H
+
+#include <Arduino.h>
+#include <ESP8266WiFi.h>
+#include <WiFiUdp.h>
+
+class UdpContext;
+
+#define SSDP_UUID_SIZE              37
+#define SSDP_SCHEMA_URL_SIZE        64
+#define SSDP_DEVICE_TYPE_SIZE       64
+#define SSDP_FRIENDLY_NAME_SIZE     64
+#define SSDP_SERIAL_NUMBER_SIZE     32
+#define SSDP_PRESENTATION_URL_SIZE  128
+#define SSDP_MODEL_NAME_SIZE        64
+#define SSDP_MODEL_URL_SIZE         128
+#define SSDP_MODEL_VERSION_SIZE     32
+#define SSDP_MANUFACTURER_SIZE      64
+#define SSDP_MANUFACTURER_URL_SIZE  128
+
+typedef enum {
+  NONE,
+  SEARCH,
+  NOTIFY
+} ssdp_method_t;
+
+
+struct SSDPTimer;
+
+class SSDPClass{
+  public:
+    SSDPClass();
+    ~SSDPClass();
+
+    bool begin();
+
+    void schema(WiFiClient client);
+
+    void setDeviceType(const String& deviceType) { setDeviceType(deviceType.c_str()); }
+    void setDeviceType(const char *deviceType);
+    void setName(const String& name) { setName(name.c_str()); }
+    void setName(const char *name);
+    void setURL(const String& url) { setURL(url.c_str()); }
+    void setURL(const char *url);
+    void setSchemaURL(const String& url) { setSchemaURL(url.c_str()); }
+    void setSchemaURL(const char *url);
+    void setSerialNumber(const String& serialNumber) { setSerialNumber(serialNumber.c_str()); }
+    void setSerialNumber(const char *serialNumber);
+    void setSerialNumber(const uint32_t serialNumber);
+    void setModelName(const String& name) { setModelName(name.c_str()); }
+    void setModelName(const char *name);
+    void setModelNumber(const String& num) { setModelNumber(num.c_str()); }
+    void setModelNumber(const char *num);
+    void setModelURL(const String& url) { setModelURL(url.c_str()); }
+    void setModelURL(const char *url);
+    void setManufacturer(const String& name) { setManufacturer(name.c_str()); }
+    void setManufacturer(const char *name);
+    void setManufacturerURL(const String& url) { setManufacturerURL(url.c_str()); }
+    void setManufacturerURL(const char *url);
+    void setHTTPPort(uint16_t port);
+    void setTTL(uint8_t ttl);
+
+  protected:
+    void _send(ssdp_method_t method);
+    void _update();
+    void _startTimer();
+    static void _onTimerStatic(SSDPClass* self);
+
+    UdpContext* _server;
+    SSDPTimer* _timer;
+    uint16_t _port;
+    uint8_t _ttl;
+
+    IPAddress _respondToAddr;
+    uint16_t  _respondToPort;
+
+    bool _pending;
+    unsigned short _delay;
+    unsigned long _process_time;
+    unsigned long _notify_time;
+
+    char _schemaURL[SSDP_SCHEMA_URL_SIZE];
+    char _uuid[SSDP_UUID_SIZE];
+    char _deviceType[SSDP_DEVICE_TYPE_SIZE];
+    char _friendlyName[SSDP_FRIENDLY_NAME_SIZE];
+    char _serialNumber[SSDP_SERIAL_NUMBER_SIZE];
+    char _presentationURL[SSDP_PRESENTATION_URL_SIZE];
+    char _manufacturer[SSDP_MANUFACTURER_SIZE];
+    char _manufacturerURL[SSDP_MANUFACTURER_URL_SIZE];
+    char _modelName[SSDP_MODEL_NAME_SIZE];
+    char _modelURL[SSDP_MODEL_URL_SIZE];
+    char _modelNumber[SSDP_MODEL_VERSION_SIZE];
+};
+
+#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SSDP)
+extern SSDPClass SSDP;
+#endif
+
+#endif