Closed aaron-neal closed 4 years ago
see #4
@porkyneal
Thanks so much for the good solution. I'll test and merge soon into this EthernetWebServer_STM32 as well as EthernetWebServer, WiFiWebServer and WiFiNINA_Generic libraries.
Regards,
@porkyneal
Could you please test to see this implementation in EthernetWebServer is OK in STM32.
void EthernetWebServer::sendContent_P(PGM_P content)
{
sendContent_P(content, strlen_P(content));
}
void EthernetWebServer::sendContent_P(PGM_P content, size_t size)
{
const char * footer = "\r\n";
if (_chunked)
{
char * chunkSize = (char *) malloc(11);
if (chunkSize)
{
sprintf(chunkSize, "%x%s", size, footer);
_currentClient.write(chunkSize, strlen(chunkSize));
free(chunkSize);
}
}
_currentClient.write(content, size);
if (_chunked)
{
_currentClient.write(footer, 2);
}
}
It's not currently implemented in STM32 (my fault). That's why you experienced the issue.
@porkyneal
Which STM32 board are you using? Many STM32 (and other) boards don't support the memcpy_P command. That might be the reason I dropped the PROGMEM related commands.
Could you also test the other version (I just draft, not compile / test) to use new, instead of local buffer, to allocate memory on heap.
void EthernetWebServer::sendContent_P(PGM_P content, size_t size)
{
const char * footer = "\r\n";
if (_chunked)
{
char * chunkSize = (char *) malloc(11);
if (chunkSize)
{
sprintf(chunkSize, "%x%s", size, footer);
_currentClient.write(chunkSize, strlen(chunkSize));
free(chunkSize);
}
}
#if 1
#define SENDCONTENT_P_BUFFER_SZ 4096
uint8_t* buffer = new uint8_t[SENDCONTENT_P_BUFFER_SZ];
if (buffer)
{
uint16_t count = size / SENDCONTENT_P_BUFFER_SZ;
uint16_t remainder = size % SENDCONTENT_P_BUFFER_SZ;
uint16_t i = 0;
for (i = 0; i < count; i++) {
/* code */
memcpy_P(buffer, &content[i*SENDCONTENT_P_BUFFER_SZ], SENDCONTENT_P_BUFFER_SZ);
_currentClient.write(buffer, SENDCONTENT_P_BUFFER_SZ);
}
memcpy_P(buffer, &content[i*SENDCONTENT_P_BUFFER_SZ], remainder);
_currentClient.write(buffer, remainder);
delete [] buffer;
}
else
{
}
#else
uint16_t bufSize = 4096;
uint8_t buffer[bufSize];
uint16_t count = size / bufSize;
uint16_t remainder = size % bufSize;
uint16_t i = 0;
for (i = 0; i < count; i++) {
/* code */
memcpy_P(buffer, &content[i*bufSize], bufSize);
_currentClient.write(buffer, bufSize);
}
memcpy_P(buffer, &content[i*bufSize], remainder);
_currentClient.write(buffer, remainder);
#endif
if (_chunked)
{
_currentClient.write(footer, 2);
}
}
@porkyneal
Could you verify these mods are OK in your case.
Compile OK, not yet tested:
// For PROGMEM commands
#include <pgmspace.h>
#define memccpy_P(dest, src, c, n) memccpy((dest), (src), (c), (n))
#ifndef PGM_VOID_P
#define PGM_VOID_P const void *
#endif
void send_P(int code, PGM_P content_type, PGM_P content);
void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength);
void sendContent_P(PGM_P content);
void sendContent_P(PGM_P content, size_t size);
void EthernetWebServer::send_P(int code, PGM_P content_type, PGM_P content)
{
size_t contentLength = 0;
if (content != NULL)
{
contentLength = strlen_P(content);
}
String header;
char type[64];
memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
_prepareHeader(header, code, (const char* )type, contentLength);
ET_LOGDEBUG1(F("EthernetWebServer::send_P: len = "), contentLength);
ET_LOGDEBUG1(F("content = "), content);
ET_LOGDEBUG1(F("EthernetWebServer::send_P: hdrlen = "), header.length());
ET_LOGDEBUG1(F("header = "), header);
_currentClient.write(header.c_str(), header.length());
if (contentLength)
{
sendContent_P(content);
}
}
void EthernetWebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength)
{
String header;
char type[64];
memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
_prepareHeader(header, code, (const char* )type, contentLength);
ET_LOGDEBUG1(F("EthernetWebServer::send_P: len = "), contentLength);
ET_LOGDEBUG1(F("content = "), content);
ET_LOGDEBUG1(F("EthernetWebServer::send_P: hdrlen = "), header.length());
ET_LOGDEBUG1(F("header = "), header);
_currentClient.write((const uint8_t *) header.c_str(), header.length());
if (contentLength)
{
sendContent_P(content, contentLength);
}
}
void EthernetWebServer::sendContent_P(PGM_P content)
{
sendContent_P(content, strlen_P(content));
}
void EthernetWebServer::sendContent_P(PGM_P content, size_t size)
{
const char * footer = "\r\n";
if (_chunked)
{
char * chunkSize = (char *) malloc(11);
if (chunkSize)
{
sprintf(chunkSize, "%x%s", size, footer);
_currentClient.write(chunkSize, strlen(chunkSize));
free(chunkSize);
}
}
#if 1
#define SENDCONTENT_P_BUFFER_SZ 4096
uint8_t* buffer = new uint8_t[SENDCONTENT_P_BUFFER_SZ];
if (buffer)
{
uint16_t count = size / SENDCONTENT_P_BUFFER_SZ;
uint16_t remainder = size % SENDCONTENT_P_BUFFER_SZ;
uint16_t i = 0;
for (i = 0; i < count; i++) {
/* code */
memcpy_P(buffer, &content[i*SENDCONTENT_P_BUFFER_SZ], SENDCONTENT_P_BUFFER_SZ);
_currentClient.write(buffer, SENDCONTENT_P_BUFFER_SZ);
}
memcpy_P(buffer, &content[i*SENDCONTENT_P_BUFFER_SZ], remainder);
_currentClient.write(buffer, remainder);
delete [] buffer;
}
else
{
}
#else
uint16_t bufSize = 4096;
uint8_t buffer[bufSize];
uint16_t count = size / bufSize;
uint16_t remainder = size % bufSize;
uint16_t i = 0;
for (i = 0; i < count; i++) {
/* code */
memcpy_P(buffer, &content[i*bufSize], bufSize);
_currentClient.write(buffer, bufSize);
}
memcpy_P(buffer, &content[i*bufSize], remainder);
_currentClient.write(buffer, remainder);
#endif
if (_chunked)
{
_currentClient.write(footer, 2);
}
}
Sorry, I have been away for a few days. I wont be able to test this for a day or so.
From memory the current Non STM32 specific EthernetWebServer implementation did not work, it compiles, didn't crash, but no data was transferred.
Again, will get on it when I back on that project.
From memory the current Non STM32 specific EthernetWebServer implementation did not work, it compiles, didn't crash, but no data was transferred.
This happens because data is stored in PROGMEM and the original code doesn't support PROGMEM-related functions.
I already fixed in the previous post to add PROGMEM functions, waiting for you to test, before commit and release new version. Certainly with your contribution credit.
@porkyneal
Could you verify these mods are OK in your case.
Compile OK, not yet tested:
// For PROGMEM commands #include <pgmspace.h> #define memccpy_P(dest, src, c, n) memccpy((dest), (src), (c), (n)) #ifndef PGM_VOID_P #define PGM_VOID_P const void * #endif
void send_P(int code, PGM_P content_type, PGM_P content); void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength); void sendContent_P(PGM_P content); void sendContent_P(PGM_P content, size_t size);
void EthernetWebServer::send_P(int code, PGM_P content_type, PGM_P content) { size_t contentLength = 0; if (content != NULL) { contentLength = strlen_P(content); } String header; char type[64]; memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); _prepareHeader(header, code, (const char* )type, contentLength); ET_LOGDEBUG1(F("EthernetWebServer::send_P: len = "), contentLength); ET_LOGDEBUG1(F("content = "), content); ET_LOGDEBUG1(F("EthernetWebServer::send_P: hdrlen = "), header.length()); ET_LOGDEBUG1(F("header = "), header); _currentClient.write(header.c_str(), header.length()); if (contentLength) { sendContent_P(content); } } void EthernetWebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) { String header; char type[64]; memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type)); _prepareHeader(header, code, (const char* )type, contentLength); ET_LOGDEBUG1(F("EthernetWebServer::send_P: len = "), contentLength); ET_LOGDEBUG1(F("content = "), content); ET_LOGDEBUG1(F("EthernetWebServer::send_P: hdrlen = "), header.length()); ET_LOGDEBUG1(F("header = "), header); _currentClient.write((const uint8_t *) header.c_str(), header.length()); if (contentLength) { sendContent_P(content, contentLength); } } void EthernetWebServer::sendContent_P(PGM_P content) { sendContent_P(content, strlen_P(content)); } void EthernetWebServer::sendContent_P(PGM_P content, size_t size) { const char * footer = "\r\n"; if (_chunked) { char * chunkSize = (char *) malloc(11); if (chunkSize) { sprintf(chunkSize, "%x%s", size, footer); _currentClient.write(chunkSize, strlen(chunkSize)); free(chunkSize); } } #if 1 #define SENDCONTENT_P_BUFFER_SZ 4096 uint8_t* buffer = new uint8_t[SENDCONTENT_P_BUFFER_SZ]; if (buffer) { uint16_t count = size / SENDCONTENT_P_BUFFER_SZ; uint16_t remainder = size % SENDCONTENT_P_BUFFER_SZ; uint16_t i = 0; for (i = 0; i < count; i++) { /* code */ memcpy_P(buffer, &content[i*SENDCONTENT_P_BUFFER_SZ], SENDCONTENT_P_BUFFER_SZ); _currentClient.write(buffer, SENDCONTENT_P_BUFFER_SZ); } memcpy_P(buffer, &content[i*SENDCONTENT_P_BUFFER_SZ], remainder); _currentClient.write(buffer, remainder); delete [] buffer; } else { } #else uint16_t bufSize = 4096; uint8_t buffer[bufSize]; uint16_t count = size / bufSize; uint16_t remainder = size % bufSize; uint16_t i = 0; for (i = 0; i < count; i++) { /* code */ memcpy_P(buffer, &content[i*bufSize], bufSize); _currentClient.write(buffer, bufSize); } memcpy_P(buffer, &content[i*bufSize], remainder); _currentClient.write(buffer, remainder); #endif if (_chunked) { _currentClient.write(footer, 2); } }
This all seems to work OK. 👍
Thanks. I'll merge into master then create a new release.
Sweet, thanks.
@porkyneal
Already merged then updated the code, then release v1.0.6
Your contribution is noted in Contributions-and-Thanks, but under the name of Aron N., not Nickname porkyneal ;-)
More contributions are awaited and very welcome.
Regards,
Before I start out, would just like to say thanks for this library it has helped me very quickly port a lot of work from the ESP to the STM32 platform where I can keep both systems fairly in sync!
I also do not know if this is a generic enhancment for both this repo and the non STM32 repo, that's up to you.
I have a HTML build process using GULP which Inlines, uglifies, minifies all of my HTML, CSS and JS . The output file is a single header file that puts the entire thing into PROGMEM.
In both the ESP32 / 8266 the following is used to send this as the index:
Unforunately, this did not work with this library using the send_P command as I expected, the headers were send but no content. I have finally come up with a solution, I doubt that it is optimal, but for me it is working.
If there is a better way of doing this I would love to know, but for now I can carry on with my development, happy to offer a PR for something to work from if needed?
Now I can have my fully bootstrapped web interface that I use with my ESP devices. 👍