I have an RF frequency synthesizer project in which I enter a frequency from the keyboard to set the frequency and a small OLED display to show the entered frequency. Valid entrys in units of Hz are from 7000000 (7 MHz) to 223999999 (essentially 225 MHz).
The entered frequency is declared as:
unsigned long long ullFreq; // 64 bit uulFreq
Upon Reset, the OLED shows 'Enter Frequency' on the top line.
After entering the desired frequency the OLED shows (for example) '123456789 Hz'.
All this is working as it should. However, the tiny text on the OLED display is difficult to read with old eyes, so I decided to parse out from the ullFreq three longs, lngMhz, lngKhz, and lngHz and print them sepatated by commas. I made the string
strFreqString = String(lngMhz) + (",") + String(lngKhz) + (",") + String(lngHz) + (" Hz");
and printed it with
Serial.print(strFreqString); // print freq
Ok, this works and now the OLED shows
123,456,789 Hz as expected...all good so far...but if the entered frequency has zeros in certain positions, the display gets strange.
Examples: 123056789 shows 123,56,789 Hz
123050005 shows 123,50,5 Hz
(please note that the synthesizer is generating the proper frequencies, it is just the display that is messed-up).
It seems to me that I somehow must make the lngKhz and lngHz variables always print all three chars even tho some may be zero, ie., for example if lngKhz has the value 050, I want to print 050 and not 50.
It is a very common and acceptable pattern to compute the required size of the buffer with a call to snprintf with the same arguments into an empty buffer (this will fail, but return the necessary size), then to call sprintf as the bound check is not needed anymore. Note that 1 needs to be added by the size reported by snprintf to account for the terminal null character.
My SafeString library , available from the Arduino Library manager, has a print( ) method designed for LCD number formatting
size_t
print (double d, int decs, int width, bool forceSign=false)
Prints a double (or long/int) to this SafeString padded with spaces (left or right) and limited to the specified width.
Unlike sprintf/snprintf in which you specify a minimum width, the in the SafeString print(..,width, ..) the width is the fixed width and the result will always be that wide.
Here is an example sketch
//https://forum.arduino.cc/t/print-formating-question/886888
// download SafeString V4.1.5+ library from the Arduino Library manager or from
// https://www.forward.com.au/pfod/ArduinoProgramming/SafeString/index.html
#include "SafeString.h"
createSafeString(LCDline, 16); // or cSF(LCDline,16), for a 16 char LCD display line
void setup() {
Serial.begin(115200); //for debug
for (int i = 10; i > 0; i--) {
delay(500);
Serial.print(i); Serial.print(' ');
}
Serial.println("started");
SafeString::setOutput(Serial); // enable error msgs
}
unsigned long khz = 5;
void loop() {
if (SafeString::errorDetected()) {
Serial.println(F("Error in a SafeString operation"));
}
// clear previous formatting
LCDline.clear(); // or LCDline = "";
LCDline.print(khz, 0, 7); // format khz to take 7 chars width. 0 digits after the decimal point since input is int.
LCDline.print("Khz"); // add units
// print to LCD using char* LCDline.c_str()
Serial.println(LCDline.c_str());
delay(1000);
khz = khz * 10; // try a larger number
}
Here is the sample output, with SafeString error msgs enabled
5Khz
50Khz
500Khz
5000Khz
50000Khz
500000Khz
5000000Khz
Error: LCDline.print() width:7 too small to display even just the integer part of 50000000.00
LCDline cap:16 len:0 ''
Khz
Error in a SafeString operation
The SafeString.print( ) has a number of advantages over sprintf/snprintf
SafeString.print( ) prevents buffer overflows. sprintf will crash your code if you get the buffer size wrong. SafeString will just raise an error flag and optionally print a helpful error msg.
snprintf prevents buffer overflows (if you use the correct arguments), but just truncates the answer leading to a misleading display.
SafeString raises and error flag and returns a completely blank field if the number of too large to display as in the
Khz
above. This makes it very easy to see on the LCD that there is a problem.
If you have SafeString error messages disabled you can still check for errors using
if (SafeString::errorDetected()) {
Serial.println(F("Error in a SafeString operation"));
}
snprintf lets you check for an overflow/truncation error, BUT you have to add the code to do the check everwhere you use snprintf which becomes tiresome.
As an alternative to the suggestions already made use if(value < 100) {print 0;}
print value;
Same thing with less than 10.
Code above is to illustrate the idea, not proper code, I hope it is good enough to explain the principal.
I note that you are new here, be aware that there's a long running debate on the forum about the wisdom of using Strings (capital S) Vs using character arrays, AKA C strings (lower case s). There doesn't seem to be a resolution to this debate and it pops up in questions vaguely related to strings or Strings. Don't let this debate distract you from what you are trying to do and the useful bits of the answers you get.
@PerryBebbington
Before I posted my question I read a bunch of forum messages related to print formatting and came across the Strings vs C string debate and decided to avoid both. I'm a pretty good radio guy but a not-so-good programmer guy so I settle for 'simple' rather than 'elegant'.
I am using a variation of what you suggested:
// fix KHz
if (lngKhz < 10) { strKhz = ("00" + String(lngKhz)); }
else if (lngKhz < 100) { strKhz = ("0" + String(lngKhz)); }
else { strKhz = String(lngKhz); } // no zeros, ok as-is
// fix Hz
if (lngHz < 10) { strHz = ("00" + String(lngHz)); }
else if (lngHz < 100) { strHz = ("0" + String(lngHz)); }
else { strHz = String(lngHz); } // no zeros, ok as-is