Wish Looking for a way to get compiler not to put string constants into RAM

BertI

Member
Licensed User
As a result of some recent issues I learnt the hard way that the compiler for some reason puts literal strings (constants) into RAM even though these are immutable anyway. This seems like a waste of valuable resource even on something like an ESP8266. In searching around this topic there are numerous references to Progmem and something called the 'F' macro which on the face of it appear to provide a way to get the compiler not to put such strings into RAM. As my familiarity in this area is very poor I have no idea whether this is something that could be somehow implemented generically or via inline C within B4R, whether it would be different for ESP8266 as compared to Arduino (as implicated by the link below), or even if it is meaningful at all. However one way or another I think it would be very useful to have a user friendly way to avoid clogging up RAM for constants. One of many examples talking about the issue: https://arduino-esp8266.readthedocs.io/en/latest/PROGMEM.html
 

BertI

Member
Licensed User
Using PROGMEM is demonstrated here: https://www.b4x.com/android/forum/threads/using-progmem-to-save-memory.71114/#content
I'm not sure whether it will work with ESP8266.
Yes I've looked at this several times but can't figure out how to use this information for this context. So one might typically have the following situation:
Astream.Write("string 1")
Astream.Write("string 2")
etc

and ideally would like some function which conceptually might be written as:
Astream.Write(FromFlash("string 1"))
Astream.Write(FromFlash("string 2"))
etc

where FromFlash is a way to tell the compiler that the following string is to be held only in flash

For example seems in C you might achieve this type of thing as per this example:
void setup() {
Serial.begin(115200); Serial.println();
Serial.println( F("This is an inline string")); //
Serial.printf_P( PSTR("This is an inline string using printf %s"), "hello");
}

You'll have to excuse me if this is complete nonsense, but one conceptual way to be able to use the F or PSTR functions might be to somehow cast the whole Astream.Write statements into the block of inline C but, as I say, I don't know if such a thing is somehow possible (meaning open stream in B4R and then somehow write to this within the C.)
 

BertI

Member
Licensed User
Ok so perhaps not so elegant but the following is a possible way to use the PROGMEM functionality for a page of strings which does work on an ESP8266:
B4X:
#Region Project Attributes
    #AutoFlushLogs: True
    #CheckArrayBounds: True
    #StackBufferSize: 2000
#End Region

Sub Process_Globals
    Public Serial1 As Serial
    Public pgmstrlen As UInt
    Public pagelen As UInt
End Sub

Private Sub AppStart
    Serial1.Initialize(115200)
    Log("AppStart")
    Log("RAM: ", AvailableRAM)
    getstring("page1")
    Log("RAM afterwards: ", AvailableRAM)
    getstring("page2")
    Log("RAM afterwards: ", AvailableRAM)
End Sub

Private Sub getstring(page As String)
    GetByte(page,0)
    Dim str(pgmstrlen) As Byte
    Log("Str len = ",pgmstrlen)
    For i = 0 To pgmstrlen-1
        str(i)=GetByte(page,i)
    Next
    Log(str)
End Sub

Private Sub GetByte(page As String, Index As UInt) As Byte
    Select page
        Case "page1"
            Return RunNative("getpage1", Index)
        Case "page2"
            Return RunNative("getpage2", Index)
    End Select
End Sub

#if C
#include <avr/pgmspace.h>
const char page1[] PROGMEM = { //change the data here
"Some kind of text line stored in flash instead of SRAM\n\
and yet another line of text\n\
and blah blah blah blah blah blah blah blah blah blah blah blah\n\
and blah blah blah blah blah blah blah blah blah blah blah blah\n\
and final line of text"
};

const char page2[] PROGMEM = { //change the data here
"Some other text blah2 blah2 bla2h blah2 blah2 blah2 blah2 blah2\n\
blah2 blah2 bla2h blah2 blah2 blah2 blah2 blah2\n\
blah2 blah2 bla2h blah2 blah2 blah2 blah2 blah2\n\
blah2 blah2 bla2h blah2 blah2 blah2 blah2 blah2\n\
blah2 blah2 bla2h blah2 blah2 blah2 blah2 blah2\n\
and final line of blah2"
};

B4R::Object beo1;
B4R::Object* getpage1(B4R::Object* o) {
    b4r_main::_pgmstrlen = sizeof(page1);  
   return beo1.wrapNumber(pgm_read_byte_near(page1 + o->toLong()));
};

B4R::Object* getpage2(B4R::Object* o) {
    b4r_main::_pgmstrlen = sizeof(page2);  
   return beo1.wrapNumber(pgm_read_byte_near(page2 + o->toLong()));
};
#end if
In the above example I am deliberately retrieving the data byte by byte into a local array as the RAM space for this will be released after the getstring sub is exited. However for streaming this data out to a web page I'd probably not even fill an array first so that minimal RAM overhead is needed and I can then reduce StackBufferSize - or at least that's the theory, haven't tried yet.

Now I have another one of my dumb questions... Is there any way that I can reference the character arrays in the inline C from B4R such that I don't have to have a separate object to call for each page (as in the example above)? In other words conceptually pass the name of the string array that I want as a parameter in the RunNative call and then just one sub in the inline C which then uses this to reference the required array. I can use a global variable to pass the index as my understanding is that only one parameter can be passed via the RunNative call.

Thanks to tigrot I have seen this post https://www.b4x.com/android/forum/t...ation-and-lookup-in-progmem.88640/post-560823, which maybe has some clues of how to achieve something indirectly but I want to avoid having to copy the string data into temporary arrays and also to avoid having to maintain a table of array names in the inline C if possible. I suppose it will probably be possible to copy one byte at a time with a modified version of this example (to my present level of understanding) but just seems like another convoluted turn if there was a simpler way to directly reference the array names.
 
Last edited:
Top