Strings and Arrays - Confusion on implementation

I need the ability to display a string on LCD that corresponds to a given integer variable.
If I was using BASIC I would have done this with:

Message(100) = "100-BOTTLEOFBEER"
Message(101) = "101-DALMATIONS""
Message(128) = "256-RANDOMBYTES"

fob = 100
print Message(fob)

I have 255 strings, but the integer that chooses them can be anywhere between 100 and 999.
Obviously, not all integers have an associated string. Each string is a maximum of 16 chars long.

Any thoughts on making this work? Yes, I know the program will be huge.
Thanks in advance, Tj

Make a struct with the string ID, and then the string, and do a linear search.

Depending on what processor you have you might not be able to store them in RAM so you will have to look into putting the string data into PROGMEM. Search for that on this site.

Excuse my ignorance... could you explain that?

255 strings * 16 bytes = 4080 bytes. That's 2x the ram of a Uno and half of a Mega.

You'll want to learn how to keep them in ROM/FLASH, using PROGMEM. Particularly, you'll want to pay attention to the "Array of Strings" section:

tjohnston99:
Excuse my ignorance... could you explain that?

Example:

// Messages stored in PROGMEM to save RAM

char PROGMEM msg100 [] = "100-BOTTLEOFBEER";
char PROGMEM msg101 [] = "101-DALMATIONS";
char PROGMEM msg256 [] = "256-RANDOMBYTES";

typedef struct
  {
  const int id;
  const char * msg;
  } msgType;
  
const msgType messages [] =
  {
  { 100, msg100 },  
  { 101, msg101 },  
  { 256, msg256 },  
  };
  
// number of items in an array
template< typename T, size_t N > size_t ArraySize (T (&) [N]){ return N; }

// Print a string from Program Memory directly to save RAM 
int printProgStr (const char * str)
{
  char c;
  if (str == NULL)
    return 0;
  int count = 0;
  while ((c = pgm_read_byte (str++)))
    {
    Serial.print(c);
    count++;
    }
  return count;
} // end of printProgStr

// finds the index of a message identified by its id (linear search)
int findMessage (const int which)
  {
  for (int i = 0; i < ArraySize (messages); i++)
    if (messages [i].id == which)
      return i;
    
  return -1;   // not found
  }  // end of findMessage
  
// show one message
void printOneMessage (const int index)
  {
  if (index < 0 || index >= ArraySize (messages))
    {
    Serial.println ("Index out of range.");
    return;  
    }  // end of if out of range
    
  Serial.print ("Message: ");
  Serial.print (messages [index].id);  
  Serial.print (" = ");
  printProgStr (messages [index].msg);
  Serial.println ();
  }  // end of printOneMessage
  
void setup ()
  {
  Serial.begin (115200);
  Serial.println ();

  // print them all
  for (int i = 0; i < ArraySize (messages); i++)
    printOneMessage (i);
  
  Serial.println ("--------");
  
  int index;
  
  // lookup by id number, get message index
  index = findMessage (256);
  
  // show this message
  if (index != -1)
    printOneMessage (index);
    
  }  // end of setup
  
void loop () { }

Output:

Message: 100 = 100-BOTTLEOFBEER
Message: 101 = 101-DALMATIONS
Message: 256 = 256-RANDOMBYTES
--------
Message: 256 = 256-RANDOMBYTES

You would need to change the Serial.print to whatever you do to write to your LCD but the rest should be pretty much the same.

Looking at that now...

Thanks this will give me something to read over and help me understand it. Thank you Nick.

[quote author=Nick Gammon link=topic=154699.msg1159771#msg1159771 date=1363490215]

...

// number of items in an array
template< typename T, size_t N > size_t ArraySize (T (&) [N]){ return N; }

...

I find no reference to "template"... where do I find more info on this?

Templates are part of C++.

This is a pretty obscure bit of code that works out how many elements are in an array at compile-time, basically. Don't worry too much about it for now.

eg.

int foo [] = { 1, 2, 3 };

// number of items in an array
template< typename T, size_t N > size_t ArraySize (T (&) [N]){ return N; }

...

a = ArraySize (foo);   // <--- a will be 3

There is a bit of a write-up on Wikipedia:

If you are keen a lot of tutorials about C++ will eventually mention templates.

  int index;
  
  // lookup by id number, get message index
  index = findMessage (256);
  
  // show this message
  if (index != -1)
    printOneMessage (index);

I just want a quick print to lcd and serial.. how do I accomplish this?

tjohnston99:
I need the ability to display a string on LCD that corresponds to a given integer variable.
If I was using BASIC I would have done this with:

Message(100) = "100-BOTTLEOFBEER"

Message(101) = "101-DALMATIONS""
Message(128) = "256-RANDOMBYTES"

fob = 100
print Message(fob)




I have 255 strings, but the integer that chooses them can be anywhere between 100 and 999.
Obviously, not all integers have an associated string. Each string is a maximum of 16 chars long.

Any thoughts on making this work? Yes, I know the program will be huge.
Thanks in advance, Tj

The program errr, code could be quite small compared to the data.
Almost forgot: program = code + data. Many times debugging ends up in fixing data/input routines.

I would drop the 1st 4 chars in every string you show because you already have that information, it's the number matched to the string and a dash so just print those and then the string.

Had not thought of that. so yes, I will do that and save some space... now if I can just print them easier.

The program errr, code could be quite small compared to the data.
Almost forgot: program = code + data. Many times debugging ends up in fixing data/input routines.

I would drop the 1st 4 chars in every string you show because you already have that information, it's the number matched to the string and a dash so just print those and then the string.

now if I can just print them easier.

It takes 3 Serial.print() lines.
1st line prints the number.
2nd line prints " - " or "-".
3rd line prints the PROGMEM string which I think can be done directly since 1.0.

Put those inside a function that you pass the number and PROGMEM string pointer and you only have to code that once.

You could keep the data on an SD card in 2 files that can be generated on a PC.

....
One file would have all the strings to print.
....
The other file would have 256 4-byte (in case we want longer strings) offsets.

Using offsets let you look up directly instead of search by comparing. It's an old technique.

Each offset would either be -1 meaning there is no string for this number or >=0 meaning how many bytes into the first file the match string is located.

Then when you want to look up a number you use seek() to find the offset for that number in the second file and fetch the offset which you use seek() into the first file to get the start of the match string.

Do it that way and you can have 1000's of numbers and 1000's of match strings, even different length match strings.

Of course to do that you need SD access and to be able to make the files to use.
SD access can be done pretty cheap as DIY. Instructables and a number of pages show how.
I bought SD modules through DealExtreme that can also be found on eBay for less than $3 each.
Heck, 2G SD cards ran me $30 for 10!
OTOH you can buy a shield with SD though most of those I see do a LOT more than just SD.