[SOLVED] Reading from PROGMEM with a universal function.

Hello community,

right now im using this code to display some predifined text instead of time.

byte poiterposition;
char time1200 [] PROGMEM = {"some text 1"};
char time1215 [] PROGMEM = {"some text 2"};
char time1218 [] PROGMEM = {"some text 3"};
.
.
.
char disp [65];

const char* const string_table[] PROGMEM = {
  time1200, time1215, time1218,...};

void loop () {
  if (hh == 12 && mm == 0){poiterposition = 0;} // hh-hours, mm-minutes 
  else if (hh == 12 && mm == 15){poiterposition = 1;}
  else if (hh == 12 && mm == 18){poiterposition = 2;}
  .
  .
  .
  else{sprintf(disp, "%02d:%02d:%02d", hh, mm, ss); goto printsimpletime;}  //jumping over strcpy_P for pure time
  
  strcpy_P(disp, (char*)pgm_read_word(&(string_table[poiterposition]))); //reading from array

printsimpletime:   
DISPLAYING THE CONTENT OF disp
};

and it works BUT, creating a else if for about few hundred of cases manualy and editing them all in cases of a change is not the way it should be done.

I am thinking about printing the current time to a extra variable

char now [9];
sprintf(now, "time%02d%02d", hh, mm);

so the content of now @ 12:00 would be "time1200" the same as one pointer from string_table. Then compare if a pointer with the same name as now is availabe in the string_table. And finally read the content of time1200 from PROGMEM.

Any way realise that in code? After few days of unsuccessfull tries im at the end. :frowning:

Greetings Mike

You need to learn how to use a switch statement. It can easily handle things like that without messing with strings, which is a very inefficient way, in both CPU cycles and memory use, of doing it.

Regards,
Ray L.

RayLivingston:
You need to learn how to use a switch statement. It can easily handle things like that without messing with strings, which is a very inefficient way, in both CPU cycles and memory use, of doing it.

Regards,
Ray L.

Thank you for responding.

As far as i understand it would take a line with case each time text is displayed instead of time. This is nearly the same as creating an elseif. Right?

A switch statement will probably compile to something similar to the if/else.

What is it you are trying to accomplish? It is not obvious from the description. Do you want a different string for each quarter hour of the day? Are you trying to encode time in a string?

Think about time as a continuously incrementing series of seconds, minutes, hours or quarter hours. Breaking it up into hours and minutes just makes things more difficult, Probably.

Also do not try to optimize code before you get it working. You will go nuts. If you have performance problems then deal with them as they crop up. Don't get absorbed solving problems you may not have.

Hello HankB,

What is it you are trying to accomplish? It is not obvious from the description. Do you want a different string for each quarter hour of the day? Are you trying to encode time in a string?

There are about 200 (and there will be definitly more) different strings that are shown to a specific time. Without system. Some at @13:28 the next @13:51, @13:53, @14:16 - just an example. All the other time, the current time is displayed as digits. And yes, to display the time i have to encode it to a string what is done via the sprintf command. See code.

Also do not try to optimize code before you get it working. You will go nuts. If you have performance problems then deal with them as they crop up. Don't get absorbed solving problems you may not have.

Well, the code is running the way it is shown in the main post. Now there are elseif-s for each case of displaying text instead of time. At this point my intention is to minimise the ammount of code. Get rid of elseif-s.

What are the 200 different strings and how do they relate to time? If you can't share that it makes it hard to suggest a better solution.

Moreover, if what you have does what you need, it's not broken so why fix it? (Unless it uses too much storage to allow you to complete the rest of your sketch.)

One general solution that might be applicable is hashing. Use the time to calculate the index into your list of strings. That depends on your ability to devise a formula that will convert hours and minutes into the correct index into your table.

Thank you for staying in the topic HankB.

What are the 200 different strings and how do they relate to time? If you can't share that it makes it hard to suggest a better solution.

The strings contain simple sentences, 3 to 10 words each. The is no direct relation to time except for me. You may see it as a list of instructions to fulfill every day to the same time. Minutes are important in this case.

Moreover, if what you have does what you need, it's not broken so why fix it? (Unless it uses too much storage to allow you to complete the rest of your sketch.)

As already mentioned before there will be much more strings. There is no way around adding a line to define the string and put it to progmem. But adding an additional line for elseif steals even more memory. On the other side it will take time and resources to check all the elseif conditions.

The logic i am trying to form into code is following:

Print current time to string (char now [9]; for example "time1200") with same prefix as all the strings stored in flash memory have
--> if now matches any string name stored in flash memory --> print the string content from the flash memory.
--> if there is no match --> print the current time.

OK, I think I see now.

Would looping through the array of stings searching for a match meet your needs? Something like :

const char** stringList[] = {
"string1",
"string2",
"string3",
...
"stringn",
};

void printString(const char* stringToMatch)
{
   int i;
   for( int i=0; i< sizeof(stringList)/sizeof(stringList[0]); i++) {
     if( !strcmp( stringList[i], stringToMatch) {
       // handle match
       return;
     }
  }
  // handle no match
  return;
}

The code no longer grows as you add strings, just the string table. And the sizeof(array)/sizeof(array element) technique eliminates the need to count array elements. (Not compiled so no guarantees… And I suggest getting it working and then applying PROGMEM.)

Once agin, thank you for staying aboard.

Would looping through the array of stings searching for a match meet your needs?

To be honest one of the methods i tried was exactly the same. The problem here is, looking @your code, the whole const char** stringList will be loaded into SRAM. It would be possible on microcontrollers with higher SRAM capacities. Not on my ATmega328P.

This is also why i HAVE to use flash memory and progmem.

Print current time to string (char now [9]; for example “time1200”) with same prefix as all the strings stored in flash memory have
→ if now matches any string name stored in flash memory → print the string content from the flash memory.

It’s not possible, once the program is compiled, variables doesn’t have names… This is not Lua :slight_smile:

Here is how I would implement the event system.

This code implies you can somehow get a timestamp/time string into an integer representing a 24 hour clock. There is no need to store 32bit millisecond resolution if all you need is minutes.

Using a struct containing a 16 bit 24 hour time code and 100 characters of ‘event’ text, you get a flash usage of 20400 bytes for the events. This still leaves room for a little bit of program :slight_smile:

I only have a mega available at the moment, however this should work equally well on an Uno.

struct Event{
  int time;
  char text[ 100 ];
};

const Event events[ 200 ] PROGMEM = {
  { 1200, "This is some text." },
  { 1215, "This is some text at 12:15" },
  { 1218, "This is some more text." },
  { 1225, "This is repeated text." },
  { 1300, "This is some text at 13:00" },
  { 1330, "See, lots of text." },
  { 1400, "This is some text." },
  { 1515, "This is some text at 15:15" },
  { 1618, "This is some more text." },
  { 1725, "This is repeated text." },
  { 1800, "This is some text at 18:00" },
  { 2330, "See, lots of text." },  
};

#define sizeOf( arr ) ( sizeof(arr) / sizeof(*arr) )
typedef const __FlashStringHelper* FSH;

void setup() {
  Serial.begin( 9600 );
}

void loop() {
  //Convert your time to an integer of the time in 24 hour format.
  static int time = 1125; //11:25 AM.

  for( int i = 0 ; i < sizeOf( events ) ; ++i ){
    
    //Find event if any.
    if( time == pgm_read_word( &events[i].time ) ){
      
      //Print out event.
      Serial.print( "Event at " );
      Serial.print( time );
      Serial.print( ": " );
      
      //A sneaky trick ;)
      Serial.println( (FSH) events[i].text );
    }
  }
  
  if( ++time == 2400 ) time = 0; 
  delay( 30 ); // Time should go quickly.
}

Perhaps I misunderstood the problem. I thought you wanted to match the string contents not the name of the variable. However re-reading, you you did state name.

My last suggestion was a way to eliminate the if/then/else construct. Once working the appropriate application of PROGMEM and accessors would keep the data out of RAM.

Thanks for all the replies.

Due to pYro_65’s example I got the code up and running. ;D If someone else is looking for the same, here is the final main part of the code:

  sprintf(disp, "%02d:%02d:%02d", hh, mm, ss);
  time = hh*100+mm;
  for( int j = 0; j < sizeOf(events); ++j)
   {
    if(time != 0 && time == pgm_read_word(&events[j].time))
     {
      strcpy_P(disp, events[j].text);
     }
   }

Because of something weired disp is beeing overwritten with an empty string while time = 0. So from 00:00 to 00:01 the screens stay empty. Thats why an additional expression is added.

Greetings
Mike