Discuss here different C compiler set ups, and compiling executables for the ESP8266

User avatar
By kolban
#33041 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.
User avatar
By eldonb46
#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-2704108
Or, in general at: http://wa0uwh.blogspot.com/search/label/Esp8266

Sorry, 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

-
User avatar
By kolban
#33325 Tito,
I tried some experiments ... I coded:

Code: Select allstatic void f(char *y) {
  char *x = (const char *)"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" \      "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890" \      "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
  os_printf("%s", x);
}


And found that the section called ".rodata.str1.1" which appears to contain the constant string data remained exactly the same with and without the `const` modifier.