0

I wander what is the best option for formatting strings in Arduino for output. I mean what's a preferable way in the point of view of performance, memory usage – things like that. I see, people usually use direct Serial.print/println, like this:

  int x = 5;
  // 1st option
  Serial.print("x = ");
  Serial.println(x);

Or like this:

  // 2nd option
  Serial.println("x = " + String(x));

But what is wrong with, I believe, more traditional way of doing this:

  // 3rd optoin
  char strBuf[50];
  sprintf(strBuf, "x = %d", x);
  Serial.println(strBuf);

  // better one?
2

If you need the result in a single string then your 3rd option is the preferred way.

If you don't, then the first option of printing each part separately is the most efficient in terms of memory.

The second version, String concatenation, is the worst option in all respects and should be avoided at all costs.

I often use sprintf to a buffer if I need a specific format (for instance leading zeroes on a number).

If you want to avoid the extra code bloat of sprintf you can use various combinations of strcpy, strcat and itoa etc to build up a string into a memory buffer (if you really need to put it in a buffer).

Note that on an 8-bit Arduino sprintf has no float support - so you would still need to use dtostrf to format a float into a char buffer first, or print it directly.

2

I agree with Majenko's answer.

I created a simple CStringBuilder class to combine the first and third approach mentioned in your question. It enables to build the c-string with printf and with Print functions, which can print float or IPAddress. It is available in StreamLib in library manager.

#include <CStringBuilder.h>
#include <IPAddress.h>

const char* s = "Lorem ipsum";
char c = 'X';
int i = 42;
float f = PI;
IPAddress ipAddress(192, 168, 0, 1);

void setup()
{
  Serial.begin(115200);
  while (!Serial);

  char buff[150];
  CStringBuilder sb(buff, sizeof(buff));

  sb.print(F("Some text: "));
  sb.println(s);
  sb.print(F("Some char: "));
  sb.println(c);
  sb.print(F("HEX of char: "));
  sb.println(c, HEX);
  sb.print(F("Some integer: "));
  sb.println(i);
  sb.print(F("Some float: "));
  sb.println(f, 3);
  sb.print(F("IP address: "));
  sb.println(ipAddress);

  int l = sb.length();
  sb.print("this text doesn't fit in the remaining space in the buffer");
  if (sb.getWriteError()) {
    sb.setLength(l);
  }
  sb.println("test test");

  Serial.print("size to print: ");
  Serial.println(sb.length());
  Serial.println();
  Serial.println(buff);

  sb.reset();
  sb.printf(F("Formatted: %s;%c;%05d\r\n"), s, c, i);
  Serial.println(buff);
}

void loop()
{

}

Other classes in the library are BufferedPrint and ChunkedPrint. Both support sprintf.

2

The printf() family of functions is far preferable to me as the programmer, but in a resource-constrained environment such as the smaller AVR (and other) processors, the extra code space may not be justified or available, or the performance hit of interpreting the format string may be limiting. And sprintf(buf, ...); Serial.print(buf); also takes a stack hit for the temporary buffer (compared to just printf(...); ) It's a trade-off between code size and speed vs. you having more flexibility to write the code to the create the output you want.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.