Help on storing string to program memory

Hello, my code is sending alot of characters to a screen connected to it. my arduino is running out of ram because of this. As a remedy I replaced every Serial.print("characters"); with Serial.print(F("characters")); which freed up alot of space. Im still not done adding strings to be printed to the screen. I have once again ran out of RAM because of this. So I figure ill just F("Whatever characters are being displayed on the screen") Like so

This:

          glcd.clear();
          glcd.drawstring(20, 3, "test test test");
         glcd.display();

turns to this

          glcd.clear();
          glcd.drawstring(20, 3, F("test test test"));
         glcd.display();

However now I am getting this error and it wont allow me to do such things

Arduino: 1.0.5 (Linux), Board: "Arduino Duemilanove w/ ATmega328" AnonymousBoxArduino.ino: In function ‘void loop()’: AnonymousBoxArduino:69: error: no matching function for call to ‘ST7565::drawstring(int, int, const __FlashStringHelper*)’ /usr/share/arduino/libraries/ST7565/ST7565.h:106: note: candidates are: void ST7565::drawstring(uint8_t, uint8_t, char*)

Any ideas?

Any ideas?

Add a method to the library to use FlashStringHelper ?

You need to add the right functions to the glcd library to be able to handle flash strings - or prod the maintainer of it to add them.

majenko: You need to add the right functions to the glcd library to be able to handle flash strings - or prod the maintainer of it to add them.

Is there a simple way to convert them to a char* or something of that sort?

Why not just take a sneak peek at the source for the serial code, and copy it? We won't tell on you.

jakeschievink:

majenko:
You need to add the right functions to the glcd library to be able to handle flash strings - or prod the maintainer of it to add them.

Is there a simple way to convert them to a char* or something of that sort?

Yes you can, but it will require using a buffer. I was playing around with this a while ago and came up with two methods. One method requires a char buffer long enough to hold the longest string fetched from progmem. The other method only requires a single char buffer but has to be incorporated more intimately with code (instead of a simple function call to fill a buffer and then use the buffer as a c-string).

See this code:

/*
 * ProgmemCstrings - Techniques to read character strings from PROGMEM
 * Reference: http://www.nongnu.org/avr-libc/user-manual/pgmspace.html
 * (version "Automatically generated by Doxygen 1.7.6.1 on Tue Jan 3 2012.")
 *
 * By Chris "Sembazuru" Elliott, SembazuruCDE (at) GMail.com
 * 2013-05-27
 */

#include <avr/pgmspace.h>

// Lets set up some sample string tables in program space.
// Syntax as suggested by Hashma in http://forum.arduino.cc/index.php?topic=165518.msg1239841#msg1239841
const char PROGMEM string_1[] = "First shalt thou take out the Holy Pin, then shalt thou count to three, no more, no less.";
const char PROGMEM string_2[] = "Three shall be the number thou shalt count, and the number of the counting shall be three.";
const char PROGMEM string_3[] = "Four shalt thou not count, neither count thou two, excepting that thou then proceed to three.";
const char PROGMEM string_4[] = "Five is right out.";
const char PROGMEM string_5[] = "Once the number three, being the third number, be reached, then lobbest thou thy Holy Hand Grenade of Antioch towards thy foe, who being naughty in My sight, shall snuff it.";
// Pointer table assignment copied directly from the reference source at nongnu.org w/o thinking.
// This syntax (PGM_P) still seems to compile and work but is it considered good programming practice?
PGM_P string_table[] PROGMEM =
{
  string_1,
  string_2,
  string_3,
  string_4,
  string_5
};

void setup()
{
  Serial.begin(115200);
  while (!Serial) { // Wait for serial port to connect. Needed for Leonardo only.
  }
  delay(1000); // Simply to allow time for the ERW versions of the IDE time to automagically open the Serial Monitor. 1 second chosen arbitrarily.
  Serial.println();  // Newlines printed to separate consecutive runs on Serial Monitor
  Serial.println();
  Serial.println(F(" ***** Instructions for using the Holy Hand Grenade of Antioch, stored in PROGMEM"));
  Serial.println();
}

void loop()
{
  long startMicros = 0;  // Timer to calculate how long it takes to read and printout strings.
  long stopMicros = 0;  // Should be able to subtract out the actual serial traffic because both output will be the same.

  startMicros = micros();
  recommendedStringReferencing();
  stopMicros = micros();
  Serial.print(F(" ** The amount of microseconds to fetch and display the strings from PROGMEM using the suggested strcpy_P method is: "));
  Serial.println(stopMicros - startMicros);
  Serial.println();

  startMicros = micros();
  alternateStringReferencing();
  stopMicros = micros();
  Serial.print(F(" ** The amount of microseconds to fetch and display the strings from PROGMEM using my byte-by-byte method is: "));
  Serial.println(stopMicros - startMicros);
  Serial.println();
  while (1)
  {
    // Busy spin to basically stop execution.
    // Yeah, I could have put everything into setup() to run once and leave loop() empty, but I didn't.
  }
}

/*
 * This is the recommended method to fetch a string from PROGMEM. It does require having a buffer that is >= the longest string.
 * If one changes one of their strings, then they need to manually verify that the buffer is still long enough.
 * Tests on my UNO show at serial baud 2400 this is (repeatable) 1970044us.
 * Tests on my UNO show at serial baud 4800 this is (repeatable) 986208us.
 * Tests on my UNO show at serial baud 9600 this is (repeatable) 491924us.
 * Tests on my UNO show at serial baud 14400 this is (repeatable) 328736us.
 * Tests on my UNO show at serial baud 19200 this is (repeatable) 245964us.
 * Tests on my UNO show at serial baud 28800 this is (repeatable) 163188us.
 * Tests on my UNO show at serial baud 38400 this is (repeatable) 122980us.
 * Tests on my UNO show at serial baud 57600 this is (repeatable) 80412us.
 * Tests on my UNO show at serial baud 115200 this is (repeatable) 40208us.
 */
void recommendedStringReferencing()
{
  char buffer[200];  // Buffer to hold the characters for the suggested method of reading strings. This _should_ be longer than the longest string in progmem.

  for (unsigned char i = 0; i < 5; i++) // copied directly from the reference source w/o thinking.
  {
    strcpy_P(buffer, (PGM_P)pgm_read_word(&(string_table[i])));
    Serial.println(buffer);
  }
  return;
}

/*
 * This is an alternate method that I'm playing with. If all one wants to do with the string is push it out of the Arduino using a method that can
 * take individual bytes, why not only use a single byte buffer and read the string byte by byte, stopping when one reaches a string termination
 * character?
 *
 * This has the benefit of making code maintenance easier because one doesn't need to verify that any changed strings won't overflow a buffer character array.
 * Tests on my UNO show at serial baud 2400 this is (repeatable) 1970044us, making it within 4us (minimum micros() resolution) as strcpy_p().
 * Tests on my UNO show at serial baud 4800 this is (repeatable) 986208us, making it within 4us as strcpy_p().
 * Tests on my UNO show at serial baud 9600 this is (repeatable) 491920us, making it between 4us and 8us faster than strcpy_p().
 * Tests on my UNO show at serial baud 14400 this is (repeatable) 328736us, making it within 4us as strcpy_p().
 * Tests on my UNO show at serial baud 19200 this is (repeatable) 245960us, making it between 4us and 8us faster than strcpy_p().
 * Tests on my UNO show at serial baud 28800 this is (repeatable) 163188us, making it between 4us and 8us faster than strcpy_p().
 * Tests on my UNO show at serial baud 38400 this is (repeatable) 122980us, making it within 4us as strcpy_p().
 * Tests on my UNO show at serial baud 57600 this is (repeatable) 80412us, making it within 4us as strcpy_p().
 * Tests on my UNO show at serial baud 115200 this is (repeatable) 40208us, making it within 4us as strcpy_p().
 *
 * I wonder if it is the same method strcpy_P() uses since the timing is so similar.
 * I provide this sketch as a learning reference.
 */
void alternateStringReferencing()
{
  char buffer;  // Buffer to hold a single character to allow checking for NULL.
  unsigned int offset;  // Incrementing offset to step through PROGMEM to step through the string.

  for (byte i = 0; i < 5; i++)  // increment through my 5 strings.
  {
    offset = 0;  // (Re)set offset to 0 at the start of each string loop.
    do  // Want to read and act on one character before considering dropping out.
    {
      buffer = pgm_read_byte((PGM_P)pgm_read_word(&(string_table[i])) + offset);  // Read a single character.
      if (buffer)  // Check to see if it is any thing other than a null. A null would signify the end of the string.
      {
        Serial.print(buffer);  // Found something other than a null, so print it without a line feed.
      }
      else
      {
        Serial.println();  // Found a null, so print a line feed.
      }
      offset++;  // increment PROGMEM pointer for the current string offset for the next byte.
    }
    while (buffer);  // Loop until a null found.
  }
}