Offline
Newbie
Karma: 0
Posts: 16
|
 |
« on: January 29, 2013, 05:02:53 pm » |
Hi there, following my previous topic ( http://arduino.cc/forum/index.php/topic,145342.msg1091993.html#msg1091993) regarding the maximum number of strings I can place in an array, I am trying to use the PROGMEM to store the string arrays I have in the Flash memory; the main problem is that I don't k ow if this is the right procedure to follow in my case. What I am trying to achieve is: to have two arrays containing strings, about 300 strings each, each containing between 3 to 8 letters. Each time the sketch run, two strings are randomly selected from the two arrays (one pulled from each one of them); then they are concatenated and visualised on an OLED display, using the Adafruit library ( https://github.com/ladyada/Adafruit_CharacterOLED). As those are too many strings and I already had some memory issues with my script (I am using an Arduino Mega 2560) I would like to move those strings to the flash memory. I went to the PROGMEM tutorial page ( http://www.arduino.cc/en/Reference/PROGMEM) but it seems to me that I need to copy the string table / array to the RAM buffer. My questions are: - do I need to copy the entire array or simply the string randomly selected? - do I need to clear that buffer once I don't need the string anymore to make space for another one? Many thanks for your help, I am attaching a shorter version of my code // include the library code: #include <Adafruit_CharacterOLED.h>
// initialize the library with the numbers of the interface pins Adafruit_CharacterOLED lcd(6, 7, 8, 9, 10, 11, 12);
long randNames; // random word taken from the group male names long randLastnames; // random last name char finalWord[40]; // enough room for all strings together int myLenght; // lenght of the final word
char* maleNames[]={"FRANK", "JOHN", "MARK", "LORIS", "MICHAEL", "...., };
char* maleLastnames[]={"STEIN", "OLLIS", "GRAND", "MERLIN", "KRANZ", "...
};
char* mySpace = " ";
void setup(){ Serial.begin(9600); }
void loop(){ randNames = random (320); //picks a random name randLastnames = random (320); //picks a random last name finalWord[0] = 0; // start with a null string: strcat(finalWord, maleLastnames[randLastnames]); // add first string strcat(finalWord, mySpace); // add second string, the empty space string strcat (finalWord, maleNames[randNames]); //add third string myLenght = strlen(finalWord);
if (myLenght < 16) {// if the final word doesn't exceed 16 spaces lcd.setCursor(0, 0); //positions the cursor on the first row lcd.print(finalWord); //prints the word picked from the group
delay(10000); // wait a bit for ppl to read lcd.clear(); // clears the display delay(5000); // dark/empty display } }
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Edison Member
Karma: 9
Posts: 1001
|
 |
« Reply #1 on: January 29, 2013, 06:33:02 pm » |
You forgot a link to that OLED display. Is this the one with an SD card ? If so, you could also store the data in a file (or files) on the SD card. The Adafruit library doesn't use the standard 'stream' class which is used by many Arduino functions. That stream class can also handle strings in Flash. The Adafruit library requires the data to be in RAM. My suggestion is to use the PROGMEM tutorial. Build the two strings in a RAM buffer and write that to the display. You can use strcpy_P() or sprintf_P() to copy a string from Flash to RAM. http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.htmlDeclaring string arrays in Flash is not so easy. You could concatenate the strings with a seperation character. You could make them all 8 characters long and use it as one long string. This would be easy to implement: prog_char listOne[] PROGMEM = { "AA " \ "BBBBBBBB" \ "CCC " \ };
The normal way to do this, is to declare each name with each an own string variable in Flash. After that declare an array of pointers (in Flash) to those string names (in Flash). As far as I know, no easy way exists for this.
|
|
|
|
« Last Edit: January 29, 2013, 06:40:59 pm by Krodal »
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 16
|
 |
« Reply #2 on: January 30, 2013, 02:56:41 am » |
Thanks for the help, the OLED display doesn't have an SD card unfortunately, it is a Winstar OLED 16x2 like this model: http://www.rapidonline.com/Electronic-Components/16x2-Oled-Display-Yellow-80x36x10mm-57-2292pins are grouped differently on my model, but that is a detail. Do I need to make each string 8 characters long? I am going through the tutorial you linked, thanks for the resource! I will let you know how it goes! Many thanks Best Elso
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Edison Member
Karma: 9
Posts: 1001
|
 |
« Reply #3 on: January 30, 2013, 03:11:43 am » |
The PROGMEM tutorial is the one you need: http://www.arduino.cc/en/Reference/PROGMEMTo make each name 8 characters long, it it possible to declare it as one long string. The example is exactly the same as this (but written in a different way): prog_char listOne[] PROGMEM = "AA BBBBBBBBCCC ";
It would be one long string, only to avoid declaring both the strings and an array of strings seperately.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 16
|
 |
« Reply #4 on: January 30, 2013, 04:35:15 am » |
Okay... I still seem to have a problem understanding the strings being 8 characters long, sorry, just being thick probably  At the moment, looking at the tutorial I have created two lists and a buffer into which I copy one string at time that is being concatenated into a char, I have rewritten the previous code like this: // include the library code: #include <Adafruit_CharacterOLED.h> #include <avr/pgmspace.h>
// initialize the library with the numbers of the interface pins Adafruit_CharacterOLED lcd(6, 7, 8, 9, 10, 11, 12);
prog_char string_0[] PROGMEM = "MARK"; // "String 0" etc are strings to store - change to suit. prog_char string_1[] PROGMEM = "FRANCIS"; prog_char string_2[] PROGMEM = "JOHN"; prog_char string_3[] PROGMEM = "ANDREW"; prog_char string_4[] PROGMEM = "ROBERT"; prog_char string_5[] PROGMEM = "SEAN";
prog_char string_6[] PROGMEM = "HAYES"; // "String 0" etc are strings to store - change to suit. prog_char string_7[] PROGMEM = "LUDWIG"; prog_char string_8[] PROGMEM = "HARRISON"; prog_char string_9[] PROGMEM = "GRAND"; prog_char string_10[] PROGMEM = "SALTER"; prog_char string_11[] PROGMEM = "BOND";
// Then set up a table to refer to your strings.
PROGMEM const char *string_table1[] = // change "string_table" name to suit { string_0, string_1, string_2, string_3, string_4, string_5 };
PROGMEM const char *string_table2[] = // change "string_table" name to suit { string_6, string_7, string_8, string_9, string_10, string_11}; char buffer[30]; // make sure this is large enough for the largest string it must hold
long randNames; // random word taken from the group names long randLastnames; // random last name char finalWord[40]; // enough room for all strings together int myLenght; // lenght of the final word //int myCentre; // coordinates to place the final word in the centre
char* mySpace = " ";
void setup(){ Serial.begin(9600); }
void loop(){ randNames = random (320); //picks a synonym randLastnames = random (320); //picks an adjective finalWord[0] = 0; // start with a null string: strcpy_P(buffer, (char*)pgm_read_word(&(string_table1[randNames]))); strcat (finalWord, buffer); //add third string strcat(finalWord, mySpace); // add second string, the empty space string strcpy_P(buffer, (char*)pgm_read_word(&(string_table2[randLastnames]))); strcat(finalWord, buffer); // add first string myLenght = strlen(finalWord); Serial.println(finalWord); } This should give me the final word as a result of Names + Lastnames, one at time, I guess. Instead I cannot see anything printed on the serial monitor, for the moment I am not trying to write to the OLED. Can you spot if there is anything wrong in it? Many thanks! Elso
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Edison Member
Karma: 9
Posts: 1001
|
 |
« Reply #5 on: January 30, 2013, 05:30:10 am » |
Your code is running with a few changes. I hope you don't mind that I place the code without checking myself what changes are actually needed. You don't need to include the pgmspace.h and I use integers for random. The random needs to be 0...5, so random(6) will do that. prog_char string_0[] PROGMEM = "MARK"; // "String 0" etc are strings to store - change to suit. prog_char string_1[] PROGMEM = "FRANCIS"; prog_char string_2[] PROGMEM = "JOHN"; prog_char string_3[] PROGMEM = "ANDREW"; prog_char string_4[] PROGMEM = "ROBERT"; prog_char string_5[] PROGMEM = "SEAN";
prog_char string_6[] PROGMEM = "HAYES"; // "String 0" etc are strings to store - change to suit. prog_char string_7[] PROGMEM = "LUDWIG"; prog_char string_8[] PROGMEM = "HARRISON"; prog_char string_9[] PROGMEM = "GRAND"; prog_char string_10[] PROGMEM = "SALTER"; prog_char string_11[] PROGMEM = "BOND";
// Then set up a table to refer to your strings.
PROGMEM const char *string_table1[] = // change "string_table" name to suit { string_0, string_1, string_2, string_3, string_4, string_5 };
PROGMEM const char *string_table2[] = // change "string_table" name to suit { string_6, string_7, string_8, string_9, string_10, string_11}; char buffer[30]; // make sure this is large enough for the largest string it must hold
int randNames; // random word taken from the group names int randLastnames; // random last name char finalWord[40]; // enough room for all strings together int myLenght; // lenght of the final word //int myCentre; // coordinates to place the final word in the centre
void setup(){ Serial.begin(9600); }
void loop(){ randNames = random (6); //picks a synonym randLastnames = random (6); //picks an adjective finalWord[0] = '\0'; // start with a null string: strcpy_P(buffer, (char*)pgm_read_word(&(string_table1[randNames]))); strcat (finalWord, buffer); //add third string strcat(finalWord, " "); // add second string, the empty space string strcpy_P(buffer, (char*)pgm_read_word(&(string_table2[randLastnames]))); strcat(finalWord, buffer); // add first string myLenght = strlen(finalWord); Serial.println(finalWord); delay(1000); }
|
|
|
|
« Last Edit: January 30, 2013, 05:32:02 am by Krodal »
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 16
|
 |
« Reply #6 on: January 30, 2013, 12:30:59 pm » |
|
|
|
|
|
Logged
|
|
|
|
|
Pittsburgh, PA, USA
Offline
Faraday Member
Karma: 30
Posts: 2918
I only know some basic electricity....
|
 |
« Reply #7 on: January 30, 2013, 02:08:48 pm » |
Uses a bit less RAM but is also a bit slower: #include <avr/io.h> #include <avr/pgmspace.h>
const char PROGMEM namesTable[] = { // all this text stored in flash "MARK\0FRANCIS\0JOHN\0ANDREW\0ROBERT\0SEAN\0HAYES\0LUDWIG\0HARRISON\0GRAND\0SALTER\0BOND\0" };
PGM_P nT; // will point into namesTable
#define FIRSTNAMES 6 #define LASTNAMES 6 PGM_P firstName[ FIRSTNAMES ]; PGM_P lastName[ LASTNAMES ];
void printProgstring( PGM_P FM ) { byte fb; do { fb = pgm_read_byte( FM++ ); if ( fb ) Serial.print( fb ); } while ( fb ); }
void setup(void) { Serial.begin( 9600 );
nT = namesTable; firstName[ 0 ] = nT; byte i; // only counting to 6 so 1 byte is enough for ( i = 1; i < FIRSTNAMES; i++ ) { while ( pgm_read_byte( nT++ )); firstName[ i ] = nT++; } for ( i = 0; i < LASTNAMES; i++ ) { while ( pgm_read_byte( nT++ )); // find the \0 at the end of name[ i-1 ] lastName[ i ] = nT++; }
for ( i = 0; i < FIRSTNAMES; i++ ) { printProgstring( firstName[ i ] ); Serial.println( ); } Serial.println( ); for ( i = 0; i < LASTNAMES; i++ ) { printProgstring( lastName[ i ] ); Serial.println( ); }
}
void loop(void) { }
|
|
|
|
|
Logged
|
Examples can be found at Learning in the Main Site and at the Playground
|
|
|
|
Offline
Edison Member
Karma: 9
Posts: 1001
|
 |
« Reply #8 on: January 30, 2013, 02:26:32 pm » |
Elso, I wrote before about " You could concatenate the strings with a seperation character". The example by GoForSmoke uses a null '\0' as seperation character. GoForSmoke, I would make two concatenated strings. One for first names and one for last names. It would make the code easier to read. And perhaps the data could be written like this: const char PROGMEM namesTable[] = { // all this text stored in flash "MARK\0" \ "FRANCIS\0" \ "JOHN\0" \ "ANDREW\0" \ ............ and so on };
|
|
|
|
|
Logged
|
|
|
|
|
Pittsburgh, PA, USA
Offline
Faraday Member
Karma: 30
Posts: 2918
I only know some basic electricity....
|
 |
« Reply #9 on: January 30, 2013, 02:47:03 pm » |
I delimited with \0 so that C string functions could easily use the parts. But my 1-byte-buffer print routine could look for anything. Consider though, using \0 means it can print commas. Yeah, could go with 2 at storage but since it is the pointers that are used, I went with 1. It's only an example anyway. 
|
|
|
|
|
Logged
|
Examples can be found at Learning in the Main Site and at the Playground
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 16
|
 |
« Reply #10 on: February 02, 2013, 12:29:13 pm » |
Thank you very much indeed guys for sharing your knowledge and opinion. I can see I am simply scratching the surface of the Progmem and strings, I will solder together an new mini OLED tonight and will see if everything works correctly.
So far I have been only compiling to check that everything works, strange enough Arduino will always give me the same randomly calculated names and last names, is that normal? I mean it puts together names and last names once, then if I unplug it and plug it back either some minutes later or the day after, it gives me back names and last names with the same order it did before, as if it can only ramdomly calculate names and last names once.
Any ideas about it?
Thanks!
Elso
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Edison Member
Karma: 9
Posts: 1001
|
 |
« Reply #11 on: February 02, 2013, 01:01:31 pm » |
The random function starts with a seed and will produce the same numbers every time. To start with a different seed, use : http://arduino.cc/en/Reference/RandomSeedYou could read noise from an analog input, or the light from an ldr, or the time from a RTC, and use that for the seed.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 16
|
 |
« Reply #12 on: February 02, 2013, 01:15:03 pm » |
Thanks Krodal!
|
|
|
|
|
Logged
|
|
|
|
|
|