From 8fa156d3568eb55989a8ee8106371ce4474d2a04 Mon Sep 17 00:00:00 2001 From: Makuna Date: Fri, 24 Jul 2015 12:41:26 -0700 Subject: [PATCH] send_P, sendContent_P, and FPSTR This allows for the content of server response to be stored in flash memory. --- README.md | 21 ++++ cores/esp8266/WString.h | 4 +- .../ESP8266WebServer/src/ESP8266WebServer.cpp | 95 ++++++++++++++----- .../ESP8266WebServer/src/ESP8266WebServer.h | 5 +- 4 files changed, 99 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index db43813bb..6351e878a 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,27 @@ Both ```Serial``` and ```Serial1``` objects support 5, 6, 7, 8 data bits, odd (O 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) #### 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/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;