Combine multiple Serial.print lines without using Strings?

Hello, so I've read from many of the experts on here about the atrocities of using Strings.I'm using far too much memory as it is, so I think it is time to implement this into my code.

Right now, I'm looking for a way to change this series of lines:

     SerialBTooth.println("Learning Filter Value...Please Wait");
      SerialBTooth.println(output);
        SerialBTooth.print("Filter Value is:");
          SerialBTooth.println(Filtering3);
            SerialBTooth.print("Settling Int:");
              SerialBTooth.println(SetFilter2);

into one single line with "SerialBTooth.println"

Right now, the only way I know how to do this is to take all of the 6 lines above, and combine them into one single String and then print the string.

Is there anyway to do this without using a String? A way that is, say, advantageous for both program clarity and memory usage?

Take a look at the sprintf() and snprintf() functions

They do, however, use more memory

Take a look too at the F() macro

SerialBTooth.print(F("Settling Int:"));

Using it reduces RAM usage

Why do you want to combine the prints in the first place ?

UKHeliBob:
Why do you want to combine the prints in the first place ?

I have roughly 3 or 4 series of lines like the above. The whole code length is increased by dozens of lines as a result. I was hoping to clean up the mess a little, as it's starting to get difficult to tell who's who and where the bugs are.

Thanks for your recommendation. I'll look into it now.

The whole code length is increased by dozens of lines as a result.

You could shorten the length of the source code by using a function to output data passed to it. By reusing code in that way it would also shorten the actual code too

Worked like a charm! For the next guy, those 6 lines became:

SerialBT.printf("Learning Filter Value... Please wait... The Filter Value is: %d . The Settling Integer is: %d .", Filtering3,SetFilter2);
SerialBT.println("");

It's not quite perfect yet. I wanted the line to end there, so I added a new print line that printed nothing, but ended the line. Is there a better way to do that?

Either way, props for the advice!

Agreed on your point about using a function. I think I'll use something like the above in a function, and then call it all 4 times that I need to print things.

Many thanks!

Here is an example from my own code of using sprintf to combine 3 strings into one and send the result to an OLED. You should be able to adapt it to what you are trying to do. The next to the last line is the one I really wanted you to see. You can use change the last line to output to whatever serial device you are using. The rest is for context so you can see what it is doing.

char timestring[12];
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(WHITE);
//Line 0  
  display.setCursor(0,0);
  sprintf(timestring, "%s %1d %s", (days[weekday(t_now) - 1]), (day(t_now)),(months[month(t_now) - 1]));
  display.print(timestring);

As an aside, if someone can tell me if the names days of the week and months exist already in TimeLib and how to access them I'd appreciate it.

[Edit, obviously posted too late!]

I wanted the line to end there, so I added a new print line that printed nothing, but ended the line. Is there a better way to do that?

Put a "\n" on the end of the string

Which Arduino board are you using ?

UKHeliBob:
Which Arduino board are you using ?

None! I'm using an ESP32 WROOM

@Perry - You did it like one of the websites I saw, with the additional declaration of a char array. Is this any better or worse?

For those keeping score, the 6 lines of code are now just one line:

SerialBT.printf("Learning Filter Value... Please wait... The Filter Value is: %d . The Settling Integer is: %d .\n", Filtering3,SetFilter2);

You did it like one of the websites I saw, with the additional declaration of a char array. Is this any better or worse?

'Better' or 'worse' is meaningless. Does it do what you want and are you happy with it?

The char array is because there needs to be an intermediate step to store the string, if there is a way to avoid that I don't know what it is, other than maybe having a global char array defined for temporary use, but I personally don't like that idea.

It's split into 2 steps because I have tried various ways to get it to work, both with display.print and serial.print, without success. sprintf is very flexible and I have yet to find something it won't do.

Does your method work? As in, does it compile and does it give the result you want? I've not used BT so I am not familiar with the classes and functions defined for it in the associated libary.

printf is not available in the official Arduino core API. A few 3rd party hardware core authors like the ESP32 folks have gone rogue and added this functionality to their core API. People who want to write portable code or are used to the standardized API won't use printf so they use sprintf, etc. instead.

pert:
printf is not available in the official Arduino core API. A few 3rd party hardware core authors like the ESP32 folks have gone rogue and added this functionality to their core API. People who want to write portable code or are used to the standardized API won't use printf so they use sprintf, etc. instead.

My understanding is this, please correct me or add additional information if necessary.

In XC8 (PIC) printf() is defined but with a stub function for sending data to whatever output device is being used. This makes sense as the destination for the data depends on the thing being deigned and how the designer implements it. I would think a universally useable printf() would be impossible, so it's left to the software writer to complete.

PIC is not Arduino, but I would think the problem is the same, so it is easier to leave it out. Serial.print() and other similar functions provide both a simple output function that's easy to use for beginners and provides a bridge from sprintf() to the output hardware, and as such it seems to me providing Serial.print() and related functions was somewhat inspired.

I should have been more specific. printf was added to the Print class. The classes that inherit from Print have to define a write() function, which is the output. So you can only use printf in the classes that inherit from the Print class, but any well written code will do this. I suspect Clefsphere's code is using the SoftwareSerial library.

Arduino didn't neglect to add printf to their Print class because it was impossible or difficult to do. You can see it's a pretty minor change that needs to be made:

Arduino intentionally left it out because they think it uses syntax that is unfriendly to beginners. It's very obvious what code that uses print() and println() is doing. This has frustrated some experienced programmers who expect to have printf available. For this reason, the authors of the ESP32, ESP8266, Adafruit SAMD Boards, and MCUdude cores all added printf on their own.

I don't have strong feelings either way about the printf debate. I think there is merit to the arguments for and against. However, the 3rd party cores making changes to the standardized Arduino core API does concern me. This will be harmful to the Arduino community by leading to the proliferation of code that is not portable for no good reason other than that the author was too lazy to write multiple print() lines.

Thank you, that's helpful.

I totally get the idea of a simple, beginner friendly print function, that's in perfect keeping with the idea of Arduino. I've never once wished I had access to printf, not in XC8 and not in Arduino. sprintf is hard work to learn, but damn useful once learnt.

sprintf is very flexible and I have yet to find something it won't do.

Well, it won't format floats without first converting them to a string

Well, it won't format floats without first converting them to a string

I thought "don't be silly, of course it does!". :confused:

Well it does in XC8, but not, apparently, the version of C that Arduino uses :(.

So how do you format a float to print when using Arduino?

float aFloat = 1234.56;
char aFloatBuffer[30];
char buffer[20];


void setup()
{
  Serial.begin(115200);
  dtostrf(aFloat, 7,  4, aFloatBuffer);
  sprintf(buffer, "the float : %s", &aFloatBuffer);
  Serial.println(buffer);
}

void loop()
{
}

Thank you :slight_smile:

pert:
I suspect Clefsphere's code is using the SoftwareSerial library.

Actually, no. These are the only libaries I'm running

#include "BluetoothSerial.h"
#include "sdkconfig.h"
#include <EEPROM.h>
#include "time.h"
#include "WiFi.h"

PerryBebbington:
'Better' or 'worse' is meaningless. Does it do what you want and are you happy with it?

Does your method work? As in, does it compile and does it give the result you want? I've not used BT so I am not familiar with the classes and functions defined for it in the associated library.

I'm happy with what my one line of code does now, yes. I was just wondering if there was some advantage to doing it your way.

Yeah, it compiles and outputs what I want like a charm! No problem. That's why I asked why you were using the char array at all.

Here's the library if you are interested. It's very handy, and allows you to use a Bluetooth port just like you would use the Serial Monitor. Not really any difference in code/rules. You do need to have a declaration of class though. Like this:

BluetoothSerial SerialBT;

I'm happy with what my one line of code does now, yes. I was just wondering if there was some advantage to doing it your way.

I can't really answer that, I have no means to compare the two. If what you have done works and you are happy with it then I think that's all that matters. If you think my way looks better in some way use it, if not keep what you have. Sometimes 'better' is down to personal preferences and I think this is one of those cases.

It turns out ESP32 and ESP8266 also have support for floating point in printf and sprintf.

Clefsphere:
arduino-esp32/libraries/BluetoothSerial at master · espressif/arduino-esp32 · GitHub

Here's the library if you are interested. It's very handy, and allows you to use a Bluetooth port just like you would use the Serial Monitor. Not really any difference in code/rules. You do need to have a declaration of class though. Like this:

BluetoothSerial SerialBT;

Thanks. I own an ESP32 but have only used it when I needed to test some code to try to help someone out so I'm not very familiar with it and not at all with the Bluetooth functionality. Your library is using the Print class, so it's no different from all the other libraries that use the Print class with respect to the printf issue.

Clefsphere:
I'm happy with what my one line of code does now, yes. I was just wondering if there was some advantage to doing it your way.

As I already explained. Using printf will make your code not compile for many of the Arduino boards. How much of a disadvantage you consider that to be depends on your intentions for your code. Your current code may already be fairly ESP32-specific due to the use of functionality only available on that microcontroller, but later you may write code that would work in many architectures. In this case, it might be worth settling on a code style that will allow portability and use that consistently through your code instead of getting used to using printf.

An easy metric you can use to compare code is to compile each and write down the flash and dynamic memory reported by the Arduino IDE. Memory usage is not quite so critical on the ESP32 as on the AVR boards, but it still may be a limited resource for some applications so it's worth trying to learn to write efficient code. Another consideration is execution speed. Sometimes comparing flash memory usage is a good indicator of relative execution speed, since the instructions that take up clock cycles also take up flash memory. However, you can also add some code to take a timestamp of micros() before the code you want to evaluate and then print the elapsed time after the code finishes.