diff --git a/cores/esp8266/WString.h b/cores/esp8266/WString.h index 531fc88c8..4c502bfe5 100644 --- a/cores/esp8266/WString.h +++ b/cores/esp8266/WString.h @@ -35,8 +35,8 @@ class StringSumHelper; // an abstract class used as a means to proide a unique pointer type // but really has no body class __FlashStringHelper; -#define F(string_literal) (reinterpret_cast(PSTR(string_literal))) - +#define FPSTR(pstr_pointer) (reinterpret_cast(pstr_pointer)) +#define F(string_literal) (FPSTR(PSTR(string_literal))) // The string class class String { diff --git a/doc/reference.md b/doc/reference.md index 2fdf1caf8..cf67e9ecc 100644 --- a/doc/reference.md +++ b/doc/reference.md @@ -78,6 +78,32 @@ Both `Serial` and `Serial1` objects support 5, 6, 7, 8 data bits, odd (O), even The Program memory features work much the same way as on a regular Arduino; placing read only data and strings in read only memory and freeing heap for your application. The important difference is that on the ESP8266 the literal strings are not pooled. This means that the same literal string defined inside a `F("")` and/or `PSTR("")` will take up space for each instance in the code. So you will need to manage the duplicate strings yourself. +There is one additional helper macro to make it easier to pass ```const PROGMEM``` strings to methods that take a ```__FlashStringHelper``` called ```FPSTR()```. The use of this will help make it easier to pool strings. +Not pooling strings... + +```С++ +String response1; +response1 += F("http:"); +... +String response2; +response2 += F("http:"); +``` + +using FPSTR would become... + +```С++ +const char HTTP[] PROGMEM = "http:"; +... +{ + String response1; + response1 += FPSTR(HTTP); + ... + String response2; + response2 += FPSTR(HTTP); +} +``` + + ## WiFi(ESP8266WiFi library) This is mostly similar to WiFi shield library. Differences include: diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp index 4a0fbd3f5..cf0121388 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.cpp @@ -129,30 +129,51 @@ void ESP8266WebServer::sendHeader(const String& name, const String& value, bool } } -void ESP8266WebServer::send(int code, const char* content_type, const String& content) { - String response = "HTTP/1.1 "; - response += String(code); - response += " "; - response += _responseCodeToString(code); - response += "\r\n"; - if (!content_type) - content_type = "text/html"; - - sendHeader("Content-Type", content_type, true); - if (_contentLength != CONTENT_LENGTH_UNKNOWN && _contentLength != CONTENT_LENGTH_NOT_SET) { - sendHeader("Content-Length", String(_contentLength).c_str()); - } else if(content.length() > 0){ - sendHeader("Content-Length", String(content.length()).c_str()); - } - sendHeader("Connection", "close"); - sendHeader("Access-Control-Allow-Origin", "*"); - - response += _responseHeaders; - response += "\r\n"; - response += content; - _responseHeaders = String(); - sendContent(response); +void ESP8266WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) { + response = "HTTP/1.1 "; + response += String(code); + response += " "; + response += _responseCodeToString(code); + response += "\r\n"; + + if (!content_type) + content_type = "text/html"; + + sendHeader("Content-Type", content_type, true); + if (_contentLength != CONTENT_LENGTH_UNKNOWN && _contentLength != CONTENT_LENGTH_NOT_SET) { + sendHeader("Content-Length", String(_contentLength).c_str()); + } + else if (contentLength > 0){ + sendHeader("Content-Length", String(contentLength).c_str()); + } + sendHeader("Connection", "close"); + sendHeader("Access-Control-Allow-Origin", "*"); + + response += _responseHeaders; + response += "\r\n"; + _responseHeaders = String(); +} + +void ESP8266WebServer::send(int code, const char* content_type, const String& content) { + String header; + _prepareHeader(header, code, content_type, content.length()); + sendContent(header); + + sendContent(content); +} + +void ESP8266WebServer::send_P(int code, PGM_P content_type, PGM_P content) { + size_t contentLength = 0; + + if (content != NULL) { + contentLength = strlen_P(content); + } + + String header; + _prepareHeader(header, code, String(FPSTR(content_type)).c_str(), contentLength); + sendContent(header); + sendContent_P(content); } void ESP8266WebServer::send(int code, char* content_type, const String& content) { @@ -178,6 +199,34 @@ void ESP8266WebServer::sendContent(const String& content) { } } +void ESP8266WebServer::sendContent_P(PGM_P content) { + char contentUnit[HTTP_DOWNLOAD_UNIT_SIZE + 1]; + + contentUnit[HTTP_DOWNLOAD_UNIT_SIZE] = '\0'; + + while (content != NULL) { + size_t contentUnitLen; + PGM_P contentNext; + + // due to the memccpy signature, lots of casts are needed + contentNext = (PGM_P)memccpy_P((void*)contentUnit, (PGM_VOID_P)content, 0, HTTP_DOWNLOAD_UNIT_SIZE); + + if (contentNext == NULL) { + // no terminator, more data available + content += HTTP_DOWNLOAD_UNIT_SIZE; + contentUnitLen = HTTP_DOWNLOAD_UNIT_SIZE; + } + else { + // reached terminator + contentUnitLen = contentNext - content; + content = NULL; + } + + // write is so overloaded, had to use the cast to get it pick the right one + _currentClient.write((const char*)contentUnit, contentUnitLen); + } +} + String ESP8266WebServer::arg(const char* name) { for (int i = 0; i < _currentArgCount; ++i) { if (_currentArgs[i].key == name) diff --git a/libraries/ESP8266WebServer/src/ESP8266WebServer.h b/libraries/ESP8266WebServer/src/ESP8266WebServer.h index 888e36b4a..bf538c3e0 100644 --- a/libraries/ESP8266WebServer/src/ESP8266WebServer.h +++ b/libraries/ESP8266WebServer/src/ESP8266WebServer.h @@ -80,10 +80,12 @@ public: void send(int code, const char* content_type = NULL, const String& content = String("")); void send(int code, char* content_type, const String& content); void send(int code, const String& content_type, const String& content); + void send_P(int code, PGM_P content_type, PGM_P content); void setContentLength(size_t contentLength) { _contentLength = contentLength; } void sendHeader(const String& name, const String& value, bool first = false); void sendContent(const String& content); + void sendContent_P(PGM_P content); template size_t streamFile(T &file, const String& contentType){ setContentLength(file.size()); @@ -104,7 +106,8 @@ protected: void _parseForm(WiFiClient& client, String boundary, uint32_t len); void _uploadWriteByte(uint8_t b); uint8_t _uploadReadByte(WiFiClient& client); - + void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength); + struct RequestHandler; struct RequestArgument { String key;