- Thu Nov 05, 2015 5:21 pm
#33190
kolban wrote:Howdy folks,
I am working on an application that has a lot of constant strings. For example:
char *hello = "hello world";
or
if (strncmp(hello, "greeting") == 0) { ...
What I seem to find is that strings are placed in a special linker "section" so we can identify them. However, when I link my application, my understanding is that the strings are placed (at runtime) in precious RAM. Is there any meaning/concept/technique to direct these into flash memory so that at runtime, we will have saved some RAM for operation.
This is what I use to place "things" in Flash, but of course, they are copied to RAM as they are need.
Code: Select all
// ERB - Force format stings and string constants into FLASH Memory
#define E(x) F(x)
#define sE(x) String( F(x) )
// Used as an F() when being used as the first Element
// of a Multi-Element Expression
char _gFmtBuf[32+1];
// Buffer, Used with cF() to store constants in program space (FLASH)
#define cE(x) strncpy_P(_gFmtBuf, (PGM_P)PSTR(x), sizeof(_gFmtBuf))
// Used with printf() for the char format string
I use E() instead of F() directly, so that I can insert DEBUG info into the MACRO if and when I desire.
As stated above, the sE() is used when it is the "first element of argument" that is followed by a concatenation of the second element, see example below.
And, as stated above, the cE() is used where a "char" is need for the function arg.
I use these MACROS as shown below, these codes are just an excerpts from my Web Server App, and shown here as FLASH examples of my code.
See: my Web Server App at:
https://hackaday.com/2015/09/05/esp8266-web-server-farm/#comment-2704108Or, in general at:
http://wa0uwh.blogspot.com/search/label/Esp8266Sorry, some lines may wrap in the Forum's displays, it looks much better with single formatted long lines in the Arduino IDE. For the Forum, I have tried to perverted the code examples to avoid most of the wrap lines (hopefully I have not introduced errors).
Code: Select all // Verbose Status Info
if (gVerbose == true ) {
sz += wprintln( );
sz += wprintln( E("<!-- Verbose -->") );
sz += wprintln( E("<ul>") );
sz += wprintln( E("<b>Verbose List:</b>") );
sz += wprintln( E(" <ul>") );
sz += wprintln( sE(" Uptime: <b>")
+ String(upTimeStr()) + E("</b><br>") );
sz += wprintln( sE(" Batt Voltage: <b>")
+ String(readvdd33()/1000.0, 2) + E("V</b><br>") );
sz += wprintln( sE(" Free Heap Size: <b>")
+ String(ESP.getFreeHeap() / 1000.0, 3)
+ E("KB</b><br>") );
sz += wprintln( sE(" Hits: <b>") + String(gHits) + E("</b><br>") );
sz += wprintln( sE(" Unit ID: <b>")
+ String(ESP.getChipId() / 10000.0, 4) + E("</b><br>") );
sz += wprintln( sE(" STN IPA: <b>")
+ String(ipa2str(WiFi.localIP())) + E("</b><br>") );
sz += wprintln( sE(" My IPA: <b>")
+ String(ipa2str(gServer.client().remoteIP()))
+ E("</b><br>") );
sz += wprintln( sE(" Sketch Rev: <b>") + String(gRev) + E("</b><br>") );
sz += wprintln( E(" </ul>") );
sz += wprintln( E("</ul>") );
sz += wprintln( E("<br>") );
}
And,
Code: Select all// Converts MAC Address to String
String
ICACHE_FLASH_ATTR
mac2str( byte mac[6] )
{
snprintf(gTmpBuf, sizeof(gTmpBuf),
cE("%X:%X:%X:%X:%X:%X"), mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return gTmpBuf;
}
And more,
Code: Select all // Build Chunk
long i = 0;
#ifdef OPT_USE_CHUNKED_PROTOCOL
i = snprintf(ioChunk, 7, cE("%04X\r\n"), (int)size2Send);
#endif
memcpy(ioChunk + i, apBuf + pIndex, size2Send);
i += size2Send;
#ifdef OPT_USE_CHUNKED_PROTOCOL
memcpy(ioChunk + i, cE("\r\n"), 2);
i += 2;
#endif
And,
Code: Select all#include <pgmspace.h>
#define ALIGN __attribute__ (( aligned ( sizeof(char*) ) ))
//#define ALIGN __attribute__ (( aligned (__BIGGEST_ALIGNMENT__) ))
#define INFLASH PROGMEM ALIGN
Code: Select all// ESP Favicon Image
const char gFavicon01[] INFLASH = {
0X00, 0X00, 0X01, 0X00, 0X02, 0X00, 0X10, 0X10, 0X00, 0X00, 0X01, 0X00,
0X20, 0X00, 0X68, 0X04, 0X00, 0X00, 0X26, 0X00, 0X00, 0X00, 0X10, 0X10,
0X00, 0X00, 0X01, 0X00, 0X20, 0X00, 0X68, 0X04, 0X00, 0X00, 0X8E, 0X04,
.
.
0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00, 0X00,
0X00, 0X00
};
And,
Code: Select all const char COPYRIGHT3[] INFLASH = R"(
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 'AS IS' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
)";
I try to use "local" variable (on the stack) as much as possible (to avoid RAM). And, only when necessary, use global variables (RAM) which are marked with a leading "g". And to be consistent, function args are marked with a leading "a".
I am not sure I completely understand or address your concern, but this all works for me !!
BTW, A very Nice BOOK!, thanks.
Eldon
-