4

I was creating Arduino function which sends two int and additional chars between them and at the beginning and end of char array - then print that array to the serial port in almost real time. (For example "X50Y100T".)

I don't want to use String object in Arduino and I found two possible solutions:

  • Using sprintf():

    char sendBuffer[16];
    void dataSend(int first, int second) {
      sprintf(sendBuffer, "X%dY%dT", first, second);
      Serial.println(sendBuffer);
    };
    
  • Or something like this:

    char sendBuffer[16];
    void dataSend(int first, int second){
      char convert[6];
      sendBuffer[0] = 'X';
    
      itoa(first, convert, 10);
      strcat(sendBuffer, convert);
    
      sendBuffer[strlen(sendBuffer)] = 'Y';
    
      itoa(second, convert, 10);
      strcat(sendBuffer, convert);
    
      sendBuffer[strlen(sendBuffer)] = 'T';
    
      Serial.println(sendBuffer); 
      memset(sendBuffer,0,sizeof(sendBuffer));
    };
    

My question is - which one is better from technical point of view?

The sprintf() looks better but it increases the sketch size by ~1,5KB. Are there any other drawbacks of sprintf()? The second solution do many things to achieve same thing and I don't know if it's efficient... Maybe there are other solutions for sending this kind of char array?

4

Why do you feel you need to use either method? Neither are needed. Just print the individual parts:

Serial.print('X');
Serial.print(first);
Serial.print('Y');
Serial.print(second);
Serial.println('T');

No intermediate buffers, just sending the data direct out to the UART.

  • It really does the same thing? Great. If it has the same or better performance I will use this :) – ErnestW Dec 4 '15 at 0:04
  • 1
    When you send to the serial it all gets put into a circular buffer for sending using interrupts. It makes no sense putting it into your own buffer first to then transfer that buffer to the serial buffer. It's more efficient to just fill the serial buffer directly. – Majenko Dec 4 '15 at 0:06
  • Thanks. It makes sense :) I will use this solution from now. Probably my solutions are better for in-sketch operations. – ErnestW Dec 4 '15 at 0:16
2

Here's a method that for some reason uses slightly fewer bytes of flash memory than Majenko's does, while producing the same output:

Add #include <Streaming.h> at the beginning of the program, and replace the five statements shown in Majenko's answer with the following line:

Serial << 'X' << first << 'Y' << second << 'T' << endl;

That is, use the Streaming.h contributed library, which adds some “syntactic sugar” to Arduino C. At compile time it converts C++-like << Serial stream operators to Serial.print statements, without increasing code size. You can install it via Streaming5.zip from arduiniana.org.

When the #if-delimited sections in the program shown below are selected individually (by changing two of the three 01's at a time to 0's), I got the following compiled-code and RAM figures, running the program on a Nano, where all three ways appear to give the same output:

      Bytes  Bytes
Alt.  Flash   Ram    Note
 1    2,204   182    Five Serial.print stmts
 2    3,384   190    Using snprintf()
 3    2,176   182    One Streaming stmt

I don't know whether this slight decrease (from 2204 to 2176 bytes of flash) is replicable, but the method probably is worth trying in your situation.

The program:

#include <Streaming.h>
void setup() {
  Serial.begin(115200);
}
void loop() {
  int first=50, second=100;
  //enum { first=50, second=100};
#if 01
  Serial.print('X');
  Serial.print(first);
  Serial.print('Y');
  Serial.print(second);
  Serial.println('T');
#endif
#if 01
  char sendBuffer[16];
  snprintf(sendBuffer, sizeof sendBuffer, "X%dY%dT", first, second);
  Serial.println(sendBuffer);
#endif
#if 01
  Serial << 'X' << first << 'Y' << second << 'T' << endl;
#endif
}

Note, I used snprintf() rather than sprintf() because it's easier to avoid buffer overruns with snprintf() than with sprintf().

  • Hmm... I've never seen the streaming library. It looks great from both memory and readability point of view. Thanks for your answer :) – ErnestW Dec 4 '15 at 0:35
  • Excellent! I wished I have known this long ago. – marlar Feb 7 '18 at 22:59
  • I'm working on an attiny, and removing all calls to .print and .write, replacing them with << streaming syntax, saved me about 400 bytes. Very useful. – tedder42 May 12 '18 at 4:05
0

As a general principle code that is easy to read and modify is "better" – the problem here is that there are many "betters" to consider:

  • Readability
  • Maintainability
  • Space efficiency
  • Time efficiency

There is no universal answer to which of those is the most important. My personal bias is to start with code that is easy to read and obvious in its function. Then if time or space are an issue to start looking for ways to improve.

In your code, you could also do something like this:

  itoa(first, sendBuffer[1], 10);

Which would save you a function call and perhaps some space.

Also keep in mind that the cost of sprintf() in terms of space may get amortized over many uses, so it might not be as bad as it initially seems.

  • The sprintf() may appear two times in sketch, In this case I'm focusing on memory and performance rather than amount of lines of code. Thanks for your answer. – ErnestW Dec 3 '15 at 23:59
0

The sprintf() function has a memory cost because of the library import at compile time. But it only costs that once, no matter how many more times you use it. Unless you're running out of space, memory used is not a big deal. There's no point keeping some memory unused on an Arduino.

I think it's better to use sprintf() because:

  • sprintf() produces code that is more readable, since the final output string can be easily visualised - obviously with only place holders for the variable parts.
  • Making minor changes to the output is quicker, since there's not lines of output generator functions to re-organise.
  • You end up with less code. Less code is is easier to debug and maintain.

I would however, encourage you to use snprintf(). This function differs from sprintf() by taking the maximum buffer size as an argument. This will help prevent problems with buffer-overruns (i.e.: accidentally putting more data into a string than in can hold)

Sprintf on Arduino does not include support for floating point numbers though. This is quite a drawback. One needs to use additionally use dtostrf().

The real takeaway point is this: Only optimise your program when you need to. Donald Knuth said: "Premature Optimisation is the root of all evil".

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.