How to create efficent strings

Hi All,

I'm looking for those that know about strings in Arduino better than me to give me some guidance

The purpose of my question is to to guide me to the most efficient way to create a string from multiple variables in order to update a 4 line LCD display using LiquidCrystal_I2C.h

I'm currently using (uint8_t variables) but I need to allow for int's and reals'

But I suspect my approach is wrong in terms of program storage space and dynamic memory

        LCD_4_Line.clear();
        LCD_4_Line.setCursor(0, 0);
        DisplayString = DisplayString + "/";
        DisplayString = DisplayString + String(Month);
        DisplayString = DisplayString + "/";
        DisplayString = DisplayString + String(Year);
        DisplayString = DisplayString + " ";
        DisplayString = DisplayString + String(Hour);
        DisplayString = DisplayString + ":";
        DisplayString = DisplayString + String(Minute);
        DisplayString = DisplayString + "pm";
      LCD_4_Line.print(DisplayString);

Can anyone guide me to a better approach to this

As always any guidance given is very much appricated

Grant Brown

Give an example of the data/message that you want to show on the 4x20 LCD.

11/8/2026 4:07:37 PM

why do you need to create the String in the first place ?

You could (and should possibly) be updating each field separately when needed - no need to repaint the whole lines / screen

or just print it, like

LCD_4_Line.clear();
LCD_4_Line.setCursor(0, 0);
LCD_4_Line.print("/");
LCD_4_Line.print(Month);
LCD_4_Line.print("/");
LCD_4_Line.print(Year);
LCD_4_Line.print(" ");
LCD_4_Line.print(Hour);
LCD_4_Line.print(":");
LCD_4_Line.print(Minute);
LCD_4_Line.print("pm");

Short answer

Don't use Strings

Use arrays of chars and format them using the snprintf() function.

int Month = 12;
int Year = 2026;
int Hour = 8;
int Minute = 14;

char buffer[30];

void setup()
{
    Serial.begin(115200);
    snprintf(buffer, sizeof(buffer), "/%d/%d %d:%dpm", Month, Year, Hour, Minute);
    Serial.println(buffer);
}

void loop()
{
}

If it's just once, print each part separately. But generally

snprintf can avoid dynamic memory entirely.

If you still want to use String, first reserve the amount of space you expect to use, which attempts a single allocation. Then use concat, which has overloads for the various types (much like print)

String display;
display.reserve(30);
display.concat('/');
display.concat(month);
// etc

Both methods return a bool to indicate whether they actually worked or ran out of memory -- not that there's much you can do if it fails.

Is the some explanations as-to "/%d/%d %d:%dpm"

Grant

The % are format specifiers. This doc page is more approachable than this one. One advantage over String is being able to specify leading zeroes with widths; e.g. %02d/%02d/%02d for dates.

That is the formatting string and there is plenty of online help such as https://cplusplus.com/reference/cstdio/printf/

In the case of my example the %d parameters are placeholders for integer data items and all the other characters are concatenated with them. The data to be concatenated is in the comma separated list at the end of the snprintf() function

Short, most efficient answer is: don't.

Longer version goes: don't create a string except in the output print buffer where your artfully created string goes anyway, it's 60 chars you don't have to allocate!

LCD_4_Line.print( Day ); // I presume, snippet dint say. 
LCD_4_Line.print( '/' );
LCD_4_Line.print( Month );
LCD_4_Line.print( '/' );
LCD_4_Line.print( Year );
LCD_4_Line.print( ' ' );
LCD_4_Line.print( Hour );
LCD_4_Line.print( ':' );
LCD_4_Line.print( Minute );
LCD_4_Line.print( "pm" );
// and the finished string will be in the output buffer
// before the first char finished transmission
// less code and RAM to do the same thing... efficient?

And just for your own good: while you're in tiny memory space environments like most Arduino but for sure AVR-duino, stay clear of C++ String objects and every other C++ Container Class as well as C++ dynamic memory allocation. Avoid that like ebola.

AVR's and most other MCU's are tight spaces to code in. Sure, you can do what the big machines do.. just not fast or much at a time and practice Wasting Space won't show ways to get the most of the board and yourself.

Here is how I format the output from a DS3231 RTC.

/********************************************************************
*
* Convert data/time to string
* 
*********************************************************************/ 
String DtToString(const RtcDateTime& dt){
  char datestring[20];
  snprintf(datestring, 
      20,
      "%04u-%02u-%02u %02u:%02u:%02u",
      dt.Year(),
      dt.Month(),
      dt.Day(),
      dt.Hour(),
      dt.Minute(),
      dt.Second() );
   return String(datestring);
}

Why format an array of chars then cast it to a String ?

The callers are using it as part of other String processing. It is not on an UNO R3 - more ram is available. On an R3 it would probably be better to use a C string for the output and pass a pointer to it.

Thank you everyone

You all got me going down the right track

so again, Thank you

Grant Brown

Discard Stiring() and do not build "strings of characters."

Also, do not repeatedly print static information, for example, your "/", " ", ":", "pm" et c.).

Create a static template by placing the static information ONE TIME.

Position the cursor ONLY for displaying NEW INFORMATION.

if (now.minute() != oldminute) // minute has changed
{
  oldminute = now.minute();    // store current minute
  position_cursor(MINUTE);     // use a function to position minutes
  display_data(oldminute);     // send current minute to be displayed
}

Or, in a smaller context, cut yourself one global chracter array big enough to hold temporary values that get filled in by functions like DtToString(), and pass nothing but dt). If there is no global "now" concept like dt holds the current RtcDateTime& you might not even pass that.

a7