Good info, but if I am forced to use an older board such as an uno, nano, pro, etc then I tend to go the sprintf route as it doesn't require loading additional libraries and achieves the same thing. Bonus, the code works on pretty much anything(no outside library dependence that may not support your new MCU). And I know you mentioned that Serial.printf works on the ESP8266/32, but it also supported on a lot of the newer development boards out there as well (though your mileage may vary when trying to print floats/doubles). But as I mentioned earlier, if you stick with sprintf and Serial.print, I don't think you'll ever run into a system it fails on. It may be an extra line to print(over printf), but it is still far better than what you showed at the beginning of the video.
I am with you on using sprintf() and no extra library Using the library suggested in the video and printf() on an AVR system such as a Uno or Nano also eats into memory which you need to take account of. The standard sprintf() also eats into memory but to a lesser degree Horses for courses
TBH using _printf_ on an ESP32/8266 is a no-brainer. It's well implemented by Espressif and they use it all the time in their own code so who am I to argue with that? Standard C++ function. On the Arduino, I don't like _sprintf_ because it requires the (potentially) beginner coder to understand that buffers can overflow (very easy to do) with results that are either catastrophic or subtle and insidious. I decided to promote the library by EmbeddedArtistry because it's about as small as you can get and you can get it smaller by switching off some functionality (such as floats). It also gets the coder to use a standard C++ construct without worrying about buffer overflow. For more advanced coders like yourself please do continue using whatever you think works best and with which you're comfortable. But I'd still nudge you to at least try the library and see how much space it uses when compiled into your sketch! Great to hear your thoughts, thanks for posting.
Those who can read should check out the books... Although, the BBC series was quite faithful to the written work. If you understand floats, they work quite well. The computer uses binary -- not decimal -- so decimal values must be converted to binary before they are used/stored and the results must be converted back to decimal before printed. This is entirely different from calculators that use binary coded decimal (BCD). Only 24/32 bits are float precision; the other 8/32 bits are for the exponent. printf() has about 2 kBytes of program memory overhead, so for microcontrollers smaller than the ATMega328p, using that might limit your program size.
Indeed, I have to admit that I like the BBC series of HGTTG best of all! You say printf() takes 2K of memory; by reducing support for some of the more intensive formats this footprint can be reduced. It's all in the library's read me: github.com/embeddedartistry/arduino-printf
I use sprintf() a fair bit on both the Arduino and esp32. Didn't know I could use printf() on the esp32, but certainly will now. And I think I'd better download this arduino printf library too. You always teach me something new Ralph. Thank you Sir. Watching on my phone (as always).
The reason the original Arduino library omitted printf was the standard implementation is huge, and would consume an unfeasible amount of the ROM and RAM space on the ATMEGA324 used on the original arduinos. Marco's implementation is much smaller making it practical on an Uno. Of course, in reality most simple Arduino projects don't use much code space, so the convenience of printf may be warranted, especially while debugging. Its a lot easier to comment out a line or 2 of debug statements than huge blocks of code!
That implies, Ian, that Arduino were not capable of writing a small, compact printf library for the Arduino - just like Marko's one I'm now demonstrating! Oh well, perhaps they had bigger fish to fry. Even so...
@@RalphBacon The library you reference takes quite a lot of memory. I did a test with code that just adds 2 int variables and stores the result in a third int variable on an Uno. Here are the compile details code with no serial 444 bytes mem 9 bytes for global variables code with Serial.print 1696 mem 188 GV code with LibPrintf 6024 mem 242 GV As Ian says not a big deal with most of our projects. Just something to keep in mind.
@@RalphBacon The Arduino environment is basically a full C++ compiler and libraries taken from the Processing and Wiring projects with some scripting mixed in. So the Wiring/Processing guys just decided to omit printf (which takes 0 hours of coding) rather than implement a cut down version since its not really essential to the core project and they were targetting microcontrollers with very small memories.
Hey Ralph, congrats on another nice video. Since you're on the subject of Serial.print, Debug, printf, etc. you might be interested in this library: Streaming by Mikal Hart. I never write a program without it because instead of all those repetitive Serial.print statements, you can do this: Serial
Yes, that's the way you would do it in a "standard" (ie non-Arduino) C program. I don't think the Arduino team ever implemented it which is probably why Mikal has now introduced it, good job!
alternately, without having to include a library: char pout[250] = {}; // at the top of your code, globally. // later in your code sprintf(pout, "Iteration %i has a value of %li (derived from %6.10f)", intValue, longValue, floatValue); Serial.println(pout); The length of your line is limited to the size of the pout array (250 chars).
Well, OK, but imagine yourself an Arduino noob. Do you really think that person will understand that code (not just copy and paste)? And, horror, you're still using Serial.print, the very thing we're trying to get away from and back to standard C++. Two statements where one will suffice.
Great video. This library has proven to very useful in my projects. However I have 2 caveats for you viewers using PlaformIO. If one uses PlatformIO library manager to install this library, for some unexplained reason, the folder "Extras" which is referred in the LibPrintf .cpp and .h files, is not installed and therefore the project fails to compile. The solution is to download it from GitHub and manually install it. The second caveat is with the latest version of the library, V1.2.6. Although the library should use the Serial stream as its default, it does not. One has to initialize the printf object with the command "printf_init(Serial) in the setup function. Interestingly these "undocumented features" are not present when one uses the Arduino IDE. Thanks again for your great video.
Excellent update for PlatformIO users, Jacques; thank you for taking the time and trouble to document it all here. 👍 I'm sure there will be many viewers who find this comment (once Google has trawled it) 👌
Many thanks for this update. I am certainly going to take full advantage of 'printf' as you describe it in this video. I have always hated Arduino IDE for NOT including this feature. My last comment: your thumb had an "issue" which I assume hs been 'sorted'. Like the new video format you are using, too. UPDATE: Using VSC & PlatfromIO, I placed in my project folder's /lib folder, a folder /LibPrintf which has LibPrintf.cpp and LibPrintf.h. AND I had to include the /extras folder, too. So far this is brilliant and you 'prodding' has done the trick. However, when I had PlatformIO download and install the LibPrintf library in my project folder, it couldn't find the /extra folder it said. VERY CURIOUS!
Indeed, printf is very useful. I use this on PlatformIO all the time but I don't recall the 'extras' folder being an issue (or being present, if it comes to that). I might have installed the library via PlatformIO (ie an entry in platformio.ini), I just can't remember especially as when using ESP32s you don't need this library at all, it's all built in to the framework.
FYI Ralph PI is declared as a global variable in the Arduino.h header so you can use it, as well as: #define PI 3.1415926535897932384626433832795 #define HALF_PI 1.5707963267948966192313216916398 #define TWO_PI 6.283185307179586476925286766559 #define DEG_TO_RAD 0.017453292519943295769236907684886 #define RAD_TO_DEG 57.295779513082320876798154814105
At 15:24 you say %s is a C++ string but it is a actually a C string. C++ is happy with string objects and supports c strings only for backward compatibility. It is stated plainly on the page you referenced "Writes the C string pointed by format to the standard output (stdout)."
Certainly C++ supports the object called 'string' and would be a better (?) way of dealing with character strings. Then again, a null terminated character string (char *) is well understood so is often used too.
Great work Ralph, I have been using printf since your video #168 where you introduced MCUdude minicore. He implements it natively in Serial so you can use Serial.printf(...) . What did you do to your thumb?
Yes, miniCore is great, Andrew, but I suspect that most beginners do not flash their Arduino-type device with a bootloader. Even if they do, they pick the standard board from the dropdown list. So this is an alternative way of reaping the benefits of printf without all the faf. (Technical term). My thumb? Picture in your mind's eye the delicatessen counter of your favourite supermarket. The area where they have the meat slicing machine. Now picture me in my workshop with a very sharp craft knife taking no precautions whatsoever. Yup. Same result.
@@RalphBacon did you look into the floating ground issue that I mentioned a while ago? (When two buildings are electrically connected such as your home and the new workshop.)
The new UK standard, Terry, it to NOT join a building's earth with the outbuilding's earth due to various risks (eg cable breaks) and also the potential difference between the two points due to resistance of long(er) cable runs. My workshop, for example, has an earthing rod buried next to it and to which the earth in my consumer unit is attached. There is no common earth between my house and my workshop.
I do MOST of my RUclips viewing on my phone. Some on my laptop. And some on the 50” tv in the lounge.👍 But I must correct you on your hhgttg…. 42 is not ‘the meaning of life’. It’s only a number. 42 is the answer to the great question of life, the universe and everything. The biggest supercomputer ever know, called Deep Thought , was built to calculate the answer to that question. And it came up with the disappointing result of ‘42’. So the planet builders of Magrathea were contracted to build an even more powerful computer, called The Earth, to calculate what the actual question was. Unfortunately, moments before it completed its computation it was unexpectedly demolished to make way for a hyperspace bypass.
good vid, coming back to the atmel 2560 for 1 project after using the esp for years forgot about the printf not being included. Ended up using sprintf as a quick fix
Well, sprintf is included as part of the Arduino framework but, for beginners, error prone as buffers easily overflow. Printf is better a lot of the time IMHO.
Who's not heard of The Hitchhikers Guide To The Galaxy? When I was on a trip to London in 1999, I managed to get the BBC radio play on cassette tapes :-) I had failed to find it in Norway, but was happy when I got it :-)
On "cassette tapes" you say, Thornberg? I've heard of those. Weren't they something used before CDs? Except they gave you added hiss for free, and if that wasn't bad enough got tangled up inside your car player on a hot day. 🤦♂️
AH! found a SOLUTION! Yes a buffering issue. I can use fflush(stdout); after the printf statements that don't have on them that works with my ESP32 dev board. reading in C++ reference there is also the option for setvbuf but I don't pretend to understand that one! both methods can cause performance issues but for my simple test sketch fflush is fine! Hi Ralph, Oddity with the escape sequence! I'm running a small sketch with a matrix keypad on and ESP32 dev board. keypad all works fine so I had a go with printf (native to ESP32 so no library involved) My code displays keypress to the console without the and a counter sends a after every 5 keypress's. Problem is nothing appears on the serial monitor or cool-term until the 5th keypress. unlike your last example in the video. Seems to be an issue with the native ESP32 implementation not flushing to console output until a is issued. How do I do that? and ideas?
On an ESP32 it's far better to use log_x where "x" is v(verbose), d(debug), i(information), w(warning) and e(fatal error). Example: log_v("Reading temperature sensor"); Basically, Espressif have massaged this "log_" macro to use the underlying printf to allow more granular messages, depending on what you have set the Core Debug Level to in Tools>Debug Level (0=no debug, 5=verbose). One beneficial side-effect is that you _don't_ need to append to every line! Oh, and you get the function from where it was called and the date/time too!!! All good and very useful. That said, there is nothing wrong with you using the built-in printf, but the Arduino Serial Monitor does expect a line-feed/carriage return (or some combination) before it outputs the data. As I now only have Arduino IDE 2.0.1 installed I can't remember if you can change this on previous 1.x versions. You could try executing Serial.flush( ) in your sketch to see whether that actually forces it out of the UART. CoolTerm (freeware serial monitor) allows you to customise this and umpteen other things and it's what I now use (using it right now!). I've liaised with Roger, the author, over the last couple of years and he's done some amazing work including some of my suggestions! freeware.the-meiers.org/ Check it out!
@@RalphBacon Hi Ralph, Yes I'm on IDE 2.0.1 so same options as you. I do plan to look and the log_ macro further down the line, my project is a IR sender for legacy kit for smart home integration (Alexa!).
At about 12:04 you say that that the value of Pi is "garbled". What actually happened is there are rounding errors. When a number is stored in a variable it is converted to a binary representation of the number. When it is converted back to a decimal string representation for printing there is typically a rounding error. You can get a value closer to what you are expecting if you employ some rounding and truncate off a couple of the least significant digits. There is no floating point unit in a simple microprocessor so floating point operations are much slower than integer operations. This is the more typical reason for avoiding floats where possible.
You are probably aware that my stance on floating point values are that they are the spawn of the Devil. Use integers wherever possible and divide by the appropriate amount (10, 100, 100...) at the point of displaying the value.
If you want Serial.print() to print PI to 8 places use Serial.print(PI,8), the printf library uses too much space (~10K) on a Nano/Uno which is what most beginners will be using. BTW l is a length specifier for printf not a long type it promotes the i or d (signed decimal integer) to the length of a long integer.
Well, I have to disagree about the space constraints. I use it on the Arduino most of my sketches (I hate those multiple Serial.print statements) and I've never had an issue. Perhaps my sketches are particularly small!
I tried to use the printf() in combo with the previously suggested DEBUG stuff, like: #define debug(x) printf(x) . . debug("Distance: %f Duration: %f ", distance, duration); . . and the compiler complained about to many parameters, b/c debug(x) wants one parameter, and I supplied 3, Somehow the full substitution didn't go as planned. Any suggestions?
I'll reply to myself... I tried #define debug(x,y,z) printf(x,y,z) and this solved the problem. So will have to use several "#define debug"s if want to print different numbers of specifiers in different parts of sketch.
Hi Ralph, here is a great Arduino function and worthy of a video. I wanted a simple way to generate 100ms and 1sec ticks for program timing, built-in timers can be used but not for any time of day code, as when they over flow and are reset there is no timer function taking place, so they become inaccurate over time. millis() is a none blocking ms counter and counts from reset up to about 50 days before rolling over. Example use, counter = millis() / 100 to get 100 ms or counter = millis() / 1000 for seconds count. Virtually any timer can be defined in code and with my Arduino nano_connect (rpi PICO) it keeps perfect time over many weeks, so negating use of RTC in some instances.
Sounds fine! But Timer0 (used for millis function) must overflow too (after 49 days) so will lose a millisecond or two at that time? I've never really looked at what happens. I would expect the maths to just keep going and the unsigned long just goes from 0xFFFFFFFF to 0x00000000 in one tick of the clock? I'm also guessing that your formula should read: millis( ) % 100=0 or millis( ) %1000=0 to get the modulo of the divisor? I say that because I use that to flash an LED: digitalWrite(ledPin, millis( ) % 1000 > 500 ? HIGH : LOW) You can change the 500 to get a briefer or longer flash every second!
@@RalphBacon Yes there is a short duration overflow at 50 days and reset to 0x000. One question for using non blocking millis()to replace the blocking delay() shown below. How could the code be rewritten to make use of the non blocking delay in millis() and allow other processing to take place while creating a delay in function1 ? function1 delay() function1 If you have already done a video for this could you point me to it?
I am fairly noob myself and I am using Serial.print and Serial.println still... To make it fit into a single line of code, I simply use the String() function which in this case would equate to: Serial.println("Iteration " + String(intValue) + " has a value of " + String(lintValue) + " (Derived from " + String(floatValue) + ")"); I admit it's not as elegant as the printf() function, but it does work in the Tinkercad simulator whilst printf() doesn't.
Well, that's fine if it works for you. ✔ Be warned, however, that using the String function (capital S) in an Arduino environment can make your heap memory look like Swiss cheese (and cause crashes) _and_ all those quoted literals are taking up room in your run time memory (SRAM). Just sayin' 😲
@@RalphBacon True enough. Thanks for taking the time to respond BTW. It's really nice when you can get an answer back from a knowledgeable person like yourself. Most of the time, youtubers don't even read, let alone respond to such comments... (no beef from me either way though: It's easy to understand why one wouldn't have the time to answer each and every viewer query...) As for the memory consideration, I'm not worried too much because I generally use the Serial.print statements for debugging only, so these don't end up in the "final" code ... I am interested in the streaming library John Toe mentioned though and will look into it. Cheers!
Thank you for explaining the use of printf, this function seems to have more powerful formatting capabilities than Serial.print, I will try it next time I need to print with an Arduino. However, I disagree with you on two points : First, I personaly don't like long printf lines (nor any long lines) in code. It is more difficult to understand what happens than with many short lines, and more difficult to modify, should you change someting later on. If one gets too many lines of code, one can always write a new function to move them elsewhere. Second, I think floating point are marvelous, provided you understand what precision they have. Arduino has only single precision, therfore it is normal that you obtain a 6 to 7 digits accuracy for you PI representation! The error was in your expectation, not in the way the number was represented ;-)
I hear you on your coding preferences, Vincent. You must code the way you want. Remember that the code in this demo was deliberately designed to show the capabilities; I'm not sure I'd right actual code like that either. But floating point variables are still flawed. No floating point number can be accurately stored in binary format; there will always be errors. Most of the time you don't notice (or don't care) but I've been bitten by them and decided the sensible option is to multiply them up to a whole number, so all the work is done with whole numbers, and then divide back down so the conversion is as accurate it can be. But if floats are working for you please do continue using them! Thanks for your thoughts and support, appreciated.
The thing you mentioned about Floats and Doubles is really interesting... How would you go about measuring a simple voltage using an analog pin and not using a Float? Or are Floats only inaccurate when you are storing a number, and then want to recall that number on various occasions? Or have I misunderstood entirely!?
If you measure a voltage using an analog input on a microcontroller, you will get an integer number. On the Arduino, as it's only 10 bits, a number from 0 to 1023 (not too accurate at the top end, I might add). For the sake of argument, if we _know_ that a reading of 512 is 2.5 volts why would be store that as a float/double when we also _know_ that we will lose accuracy. Better to store that as an integer of 25 (or 250, or 2500 depending on how many decimal places you intend working with) and only put in the decimal point when we need to display it. If we were to keep that value as a float of 2.5 it might get stored as 2.499999931 so bang goes our accuracy! Clear as mud now?
@@RalphBacon Thanks very much for taking the time to explain. Yes I understand that now. I watched this video when it came out and when you mentioned not using floats and doubles, it played on my mind ever since!
Thanks Ralph, you explain things really well. I have only just moved to PlatformIO. You seem to have a very clear Serial Monitor. Is this a PlatformIO extension and if so what is it?
The serial monitor I use (when I am not using the built-in one that comes with VSC/PlatformIO) is called CoolTerm: freeware.the-meiers.org/ It's freeware from Roger Meiers but if you use it a lot, do consider a small donation to keep motivated!
So YOU are the person watching my videos on your phone!!! Hang on, what do you mean "surprisingly" useful? Did your Dad put you up to this, Calvin? I'll be having words! Hope everyone is OK back in the Vaterland!
@@RalphBacon ONE of the many phone users 😂 well I've been using serial.print in my code for debugging and it works fine but as you've mentioned it's a lot of stuff to comment/uncomment every now and then 😁 All good over here on my end, might wanna check with dad regarding the house though 😬😅
correct me if I'm wrong. I remember using a command called " Serial.printf(); " which works similar to printf in c. what's so different between that command and this library?
If you know of an Arduino standard Serial.printf library or function do paste the link here, I'd be delighted to see it (but not the one I'm promoting by EmbeddedArtistry).
I feel lack of response to unasked question: how much does libprintf cost? I mean - just beggining Serial does cost around 1kb of space. How about this library?
The size is in the read.me page of the GitHub library. You can reduce the footprint by discarding formats you won't use (eg float). And the linker will eliminate as much as it can to keep the size down.
In C++ we should not really be using pointers at all. References are the C++ equivalent but are much safer. Obvs you _can_ still use pointers but then you are writing C not C++. I fell into this trap just a few weeks ago but have since modified my code.
@@RalphBacon If you use printf you are in fact using pointers. The site you referenced cplusplus has a page for cstdio which is the C library to perform Input/Output operations. It mentions pointers throughout. The format string is a const char * . If you use the %s format specifier than the printf function is expecting a char * type variable. In both C and C++ a char array (of one dimension) is a pointer to the zeroth element of the array. When a C string is passed to printf it is the pointer that is passed.
A nice video informative and I can see you are getting fed up with the serial.print function in your previous programming:-) did the lid drop on your Thumb:-)
Yes, you are right! I use _printf_ all the time in my ESP32 work and I use the technique I described in video #224 for Arduino work (other than just for trivial, experimental stuff).
Thanks, great tip again. When I try to combine this tip with the previous one on removing the serial.prints with the debugger to speed-up everything there is an issue with the number of arguments that is always different. Is there a workaround for that?
So you're trying to use _printf_ with the previous tip of using precompiler directives so you can switch them off? OK, David, I specifically did not mention how to do this because it can seem quite daunting. There is a construct that allows an "undefined" number of arguments to a function that you can then retrieve (it's how printf manages to do its stuff). So your #define of debugf, for example, might look like: #define debugf(x, ...) printf(x, __VA_ARGS__) *[Note: that's 2 underscores before and after VA_ARGS]* Now you can use: debugf("A number %d, and string %s", 5, "hello"); And they will disappear if you define debugf as "nothing", just as before. If you need a better example email me and I'll send you my example code.
Some modules have more than one serial port (the Mega 2560, for example). Otherwise you could use a Software Serial (it's a library) port too, but then you would need a USB-to-Serial adapter to connect to (eg an FTDI or similar) but that should be OK.
Another cell phone viewer! I will definitely have to take this into account in future videos so it is watchable on such a small device. I had no idea. I'm glad you think printf is better than Serial.print, too.
3 года назад
how about a video about not using floats? I made a solar power monitoring system and needed to use floats for Wh calculations. Is it easier to not use floats?
Not _easier_ Pawel, just more accurate. Try it yourself. Multiply a few floating point numbers together then divide by another float. Now do the same with those same numbers multiplied up into whole numbers (eg x10, x100 or x1000). Only divide at the very end of the calculation to get a floating point result. You'll see a different number. Whether that is important to you in your project only you will know.
We shall see, Gary, we shall see. But this is not really advanced. Any beginner who can write *Serial.print* can write *printf* and get added value. But I understand your point!
How spooky you are testing that using a TM1637, Leeroy, as I'm using that in my Smart Monitor Controller project - but I'm using a library. Perhaps I won't need one now?
@@RalphBacon w/o external libs for formatting that is. sprintf() and dtostrf() are standard Cpp functions that all work with Arduino framework (where it does not break them specifically). Ofc, for TM1640 and INA measuring module I am reusing public code because there it is worth a lot more effort, but you still could access registers directly for sure.
sprintf works OK w/o a library but only supports int variables (at least in AVR) dtostrf will format all variable types and is great for displays but again multiple lines of code to create the str.
@@blic-sx9ix "multiple lines of code to create the str" true, but that's a norm in C, it's a low level lang after all. I just do not consider reaching for custom lib and looking up its docs for specific use case just to avoid couple lines of code for formatting an easier way, than properly learning your standard libs. Yeah, strings are not as easy in C as in other languages, but that is a quirk of lang itself.
@@Mr.Leeroy I agree with you a few lines of code is not a bad thing. Just that Ralph's video was about reducing multi-line Serial.prints. The alternative is the library mentioned that comes with 5k+ memory footprint.
In all cases, SRAM runs fastest, program memory (flash) runs more slowly and EEPROM flash, if present, (probably) runs the slowest. Heap/stack is always allocated from SRAM but the program runs from flash. You can put (larger) variables in program flash too (use PROGMEM) which saves valuable and limited SRAM.
(disclaimert: I know you are trying not to lose beginners here, but I can't resist) With the advent of calculators we've lost the understanding of significant digits. When people calculated by hand they knew better and stopped when more didgits didn't make the answer better. In this case pi would be 3, because while 42 has 2 significant digits, 1000000 only has one. I may have that slightly wrong as I got a poor grade on the significant digits section in maths class. plus among my friends the more digits of pi you can recite the greater your status. I guess I am saying float and double have their place. 11/3
I reckon float and double would be more useful in computers if their precision was not in doubt. As my demo showed, even a number typed in directly had been compromised, simply because they cannot be accurately represented in decimal format . That said, my temperature sensor returns a float with 4 significant figures (mostly) and two digits of precision and it seems to be good. Now I'm off for some apple pi. 3 pieces. Give or take.
To get a thumb like mine you need three things: 1) a thumb 2) a very sharp craft knife 3) stupidity I'll say no more. It's almost healed now, a couple more days and the dressings will stay off, I reckon.
I tried to use the "F" with printf on an Arduino and got the following error message: ⚠ argument of type "const __FlashStringHelper *" is incompatible with parameter of type "const char *" So I guess the answer is No. And you probably won't want to anyway because strings in the F macro are const and cannot be changed; the printf macro injects run-time values. At best you could store the F strings (with placeholders) and then perhaps use those as part of the printf statement, together with the variables. Not sure that will work though. My quick play-around didn't elicit any success. 😥
@@RalphBacon Hmm. Do you think they could be stored as variables in PROGMEM and then called in to a printf with a pointer, maybe? I guess for any long text you could still use serial.print was just thinking that the F() macro for FSH does save space when you need longer strings. Thanks for having a look!
Certainly storing (and then retrieving) strings in PROGMEM that contain all the necessary placeholders would work, and might even be required on smaller processors. But it also means that your messages are predefined, no random off-the-cuff messages. Incidentally I found a page that seemed to prove this all worked with the standard snprintf and snprintf_P but I couldn't get it to work with PROGMEM - it compiled but nothing was displayed at all!
Indeed. You need to print the formatted value to a buffer string (char) and then print that formatted string to the LCD. char buffer[100]; // Ensure it is big enough! sprintf("This is a decimal %02d value", myIntVariable); lcd.setCursor (col, row); lcd.print(buffer); Make sense?
It depends on which functions you are calling. I think you will be surprised at how little it actually uses in a final project. On an ESP8266/ESP32 or even STM32 with much more memory it no longer matters.
I like the tidyness of printf over serial print, but Im not happy about giving up 1/3 of my memory for it. According to the library readme file, if memory footprint is critical, you can disable features; "For Arduino IDE, the flags need to be added to the compiler.extra_flags property in platform.txt or platform.local.txt". I found the file, but its not obvious in what way I need to edit it to disable the features Im not using or how much memory Ill be able to save. Anyone able to help?
I don't know the answer, but it might not be needed if your sketch is not already pushing the memory limits. I find I rarely use so much memory that I can't also use printf.
You can import the library below to get more (most) of the standard C++ functionality but how good/standard it is I don't know. It hasn't been touched for 10 years: github.com/maniacbug/StandardCplusplus
The order of the placeholders in each printf statement simply determines where they will appear in the final output. Look at this example where we might have two decimal values, A and B: printf("This is value A %d and this is value B %d", A, B); Note that the final A, B are placed into the string in that order. Is this what you mean?
maybe the trick of using an empty comment block at the end of a code block works in arduino, example end with a pure /* */ and then when you need to block the the code above it just need to add a /* at the beginning of your code block, the whole /* ...... /* */ is now a comment, the second /* is ignored. no need to delete the /* */ each time.
Is this a suggestion to comment out debugging statements, Jyv? I reckon we could easily tweak the precompiler trick I showed a few weeks ago to achieve the same functionality. On the ESP32 this is already built in by using log_x where x is e, w, d, v and controlled by the debugging level declaration.
I guess I missed the point of the video this relates to. But instead of faffing around with / and * you could also use a proper IDE or source code editor (Arduino IDE does not qualify for that, well at least it didn't last time I used it), which will provide a simple hotkey to (un)comment your current selection.
Uhm, working with Perl for so long time and entering Arduino world made me feel that Arduino is so neutered from usual tools that i used to use xD I understand lack of foreach() as Arduino "flavor" of C bases on, well, C. But ommiting something as basic, as taken for granted, as (s)printf gave me that feeling that i probably chose wrong hobby xD I am still a begginer on Arduino, yet i have this feeling that next wall i will painfully hit will be strftime or something similary "granted" by standard library ;p
I think once you know one language others come much more quickly. But you do end up writing C++ either in a JavaScript page or C# page or something similar (not that I have _ever_ done that). Spending 15 minutes wondering why some syntax isn't being accepted then discovering you should be writing Java not C++ is always amusing (not).
OK great to not use floats, providing they are constant. If you consider something like a macro rail that mathmatically keeps track of the camera mathmatically. To move 4 mm 12800 steps need to be taken. I want the system to move to 12.895mm, and then take a shot every 0.02mm in the opostire direction for 4.68mm. So with that a step size of 0.0003125mm calculate the number of steps neede to get to the starting postion, the number between shots and the number of steps for the diseried 4.68mm. Or I could put in the total distance i want it to cover and the number of shot and have it calulate everything else. The further to to that the system hase to do thing like calculate depth of feild, feild size and image overlap via a percentage. These are going to making very heavy use of floats as it's hard to find a way of multiplying (x10,000,000 would be the smallest possible solution -this wil cause issue when counting longer distances) out for every possible iteration and would most likely take up as much time and space as using an exteranl FPU (better yet use a chip that has one). Floating point rounding error is very well documented and every digital adding device has the same issue. I am 100% certian that the error that you get from it isn't going to effect any arduino project enough for it to be an issue - it's why they don't use them for protien folding! Honestly saying they are inacurate is like saying you don't trust your phone, your calcuator, your car, your microwave, your camera... you get the picture. It's not an issue until you get to a serious level of computing, way, way beyound what anyone will be doing with something as small as an arduino or other microcotrollers. It is also impossible to store the exact number of pi - it's an irrational number as we have not found an end to it. A float to 15dp is way more than good enough. Then the number such as 10/3, will never be accurate as we, as humans, will just round it at some point. It has no end, it repeats indefinitely. The only issue is the understanding of what is required, and what is not. Accuracy to 30dp, not normally required, for most cases 4-8dp is enough and arduino software floating point is capable of that Also Serail.print(float, Number of decimal places); //this is day one arduino stuff
If you want to use floats, especially on an Arduino that has no FP co-processor, be my guest! 😁 On an ESP32 they work better as they are built to use floats. Or, just create a float in a sketch, type in a decimal value and see what you get back out of the Arduino. If it works (well enough) for your case then by all means use them. But I'll stick to integers and divide by the relevant factor just before printing, as that way I know I'll be getting the most accurate numbers the processor can provide. Just sayin'!
@@RalphBacon great until multiplying it exceeds the interger limit. Tell me how do you multiply out 0.0003125 so it's compatible with the 480,000. Yeah multiply by 10,000,000 which then exceeded the storage of an integer. Honestly I work in metrology and can assure you the floating point rounding error is no where near as much of an issue as you think. Also esp isn't the only system with an fpu! It's only the discontinued teensys that don't example. Or you could use an fpu co-processor. Used ones with a picaxe may years ago. You do seem to have a narrow way of thinking, just sayin.
Stackoverflow had several threads on this topic but the solutions were quite involved. Have a search and see whether any of the proposed solutions (not standard C++ one, I'm afraid) meets you needs.
@@RalphBacon Yeah I have seen some threads. Tho come to think of it, it's not too hard to stuff the enum name into a corresponding char array's index.
Do you consider it a good alternative? Narrow fonts are not that easy to read (especially when doing demos). But apart from dark, light and mirage (the one I find easiest to read) what else does it give us? A choice, I suppose!
Well, not strictly true, Michael. 42 was indeed the answer that Deep Though gave (after some 7.5 million years) but indicated the question had not been properly understood by original programmers. So it built the next generation of computer (known as Earth) to answer the question, which it failed to do as we were obliterated to make way for a hyperspace by-pass just a few minutes before the answer was due. Arthur Dent, however, took some random scrabble letters to get the 42 answer to 6 times 9. Which it is, if you are working in base 13. Simples! 🤣
Well, Chris, maybe the entire audio industry doesn't use values of a different order of magnitude, or have very low value figures or they don't add up thousands of them... all of which will cause loss of precision, sometimes catastrophically. (You can even prove this in Excel). Or maybe the loss of precision is not that important in audio work, although something tells me that probably isn't the case. As you've made the effort to comment, I've dug out some further reading for you that explains the (potential) problems well: hownot2code.com/2017/06/09/float-or-double/ What do you think of that (5-minute read) article?
"once you'll do you'll never forget to do it" (about putting a ). Yea, also Perl taught me that every print(f) end with , also end of sentence is not a dot but semicolon; ;)
It's even funnier using the "log_x( )" on the ESP32. The log_x (where x is either e(rrors) w(arnings) d(ebugging) or v(erbose) which basically calls printf, automatically adds in a " " so you have to remember _not_ to include that! (OK, you can switch that off, but...)
8-bit Mega's are great for starting out, but there are the Teensy series that use Cortex CPU's that will blow the Mega out of the water With FLOPS. Serial.print(floatValue, 8); should print the float value to 7 digits including the whole number. What would be really interesting is to get printf or print to output as scientific exponential eg: 4.7e-6 But you're not crazy enough to do astrophysics on a 8-bit micro with no FPU clocked at a sloww 16MHz struggling doing single precision float math at ~78uS/op. On any Mega (328,2560) a double is 4 bytes as is a long and is at best 2x slower than int (2bytes) math. Raspberry Pi's can do double precision, have division units, and are clocked from 500..2000+MHz. So C language operations are a LOT faster on something like a Pi or Teensy (also clocked from 60..600MHz).
The ESP32 (240Mhz, dual core) is also quite nifty and can do a lot of work. Compared to the Arduino's ATMega328P... well, there is no comparison. But some complain about the ESP32's FP performance: blog.classycode.com/esp32-floating-point-performance-6e9f6f567a69
Good info, but if I am forced to use an older board such as an uno, nano, pro, etc then I tend to go the sprintf route as it doesn't require loading additional libraries and achieves the same thing. Bonus, the code works on pretty much anything(no outside library dependence that may not support your new MCU). And I know you mentioned that Serial.printf works on the ESP8266/32, but it also supported on a lot of the newer development boards out there as well (though your mileage may vary when trying to print floats/doubles). But as I mentioned earlier, if you stick with sprintf and Serial.print, I don't think you'll ever run into a system it fails on. It may be an extra line to print(over printf), but it is still far better than what you showed at the beginning of the video.
I am with you on using sprintf() and no extra library
Using the library suggested in the video and printf() on an AVR system such as a Uno or Nano also eats into memory which you need to take account of. The standard sprintf() also eats into memory but to a lesser degree
Horses for courses
TBH using _printf_ on an ESP32/8266 is a no-brainer. It's well implemented by Espressif and they use it all the time in their own code so who am I to argue with that? Standard C++ function.
On the Arduino, I don't like _sprintf_ because it requires the (potentially) beginner coder to understand that buffers can overflow (very easy to do) with results that are either catastrophic or subtle and insidious. I decided to promote the library by EmbeddedArtistry because it's about as small as you can get and you can get it smaller by switching off some functionality (such as floats). It also gets the coder to use a standard C++ construct without worrying about buffer overflow.
For more advanced coders like yourself please do continue using whatever you think works best and with which you're comfortable. But I'd still nudge you to at least try the library and see how much space it uses when compiled into your sketch!
Great to hear your thoughts, thanks for posting.
Those who can read should check out the books... Although, the BBC series was quite faithful to the written work.
If you understand floats, they work quite well. The computer uses binary -- not decimal -- so decimal values must be converted to binary before they are used/stored and the results must be converted back to decimal before printed. This is entirely different from calculators that use binary coded decimal (BCD). Only 24/32 bits are float precision; the other 8/32 bits are for the exponent.
printf() has about 2 kBytes of program memory overhead, so for microcontrollers smaller than the ATMega328p, using that might limit your program size.
Indeed, I have to admit that I like the BBC series of HGTTG best of all!
You say printf() takes 2K of memory; by reducing support for some of the more intensive formats this footprint can be reduced. It's all in the library's read me:
github.com/embeddedartistry/arduino-printf
@@RalphBacon With these formats enabled, it is 10k.
I use sprintf() a fair bit on both the Arduino and esp32. Didn't know I could use printf() on the esp32, but certainly will now. And I think I'd better download this arduino printf library too.
You always teach me something new Ralph. Thank you Sir.
Watching on my phone (as always).
So another phone viewer! Who knew? Well, everyone except me apparently. Yes, do use printf and get away from the Serial.print!
The reason the original Arduino library omitted printf was the standard implementation is huge, and would consume an unfeasible amount of the ROM and RAM space on the ATMEGA324 used on the original arduinos. Marco's implementation is much smaller making it practical on an Uno. Of course, in reality most simple Arduino projects don't use much code space, so the convenience of printf may be warranted, especially while debugging. Its a lot easier to comment out a line or 2 of debug statements than huge blocks of code!
That implies, Ian, that Arduino were not capable of writing a small, compact printf library for the Arduino - just like Marko's one I'm now demonstrating! Oh well, perhaps they had bigger fish to fry. Even so...
@@RalphBacon The library you reference takes quite a lot of memory. I did a test with code that just adds 2 int variables and stores the result in a third int variable on an Uno. Here are the compile details
code with no serial 444 bytes mem 9 bytes for global variables
code with Serial.print 1696 mem 188 GV
code with LibPrintf 6024 mem 242 GV
As Ian says not a big deal with most of our projects. Just something to keep in mind.
@@RalphBacon The Arduino environment is basically a full C++ compiler and libraries taken from the Processing and Wiring projects with some scripting mixed in. So the Wiring/Processing guys just decided to omit printf (which takes 0 hours of coding) rather than implement a cut down version since its not really essential to the core project and they were targetting microcontrollers with very small memories.
Hey Ralph, congrats on another nice video. Since you're on the subject of Serial.print, Debug, printf, etc. you might be interested in this library: Streaming by Mikal Hart. I never write a program without it because instead of all those repetitive Serial.print statements, you can do this:
Serial
Yes, that's the way you would do it in a "standard" (ie non-Arduino) C program. I don't think the Arduino team ever implemented it which is probably why Mikal has now introduced it, good job!
@@RalphBacon Well no using
Ive used the sprintf in the past but i didnt know about the *. that is pretty interesting. Thank you for the video.
Glad it was helpful!
alternately, without having to include a library:
char pout[250] = {}; // at the top of your code, globally.
// later in your code
sprintf(pout, "Iteration %i has a value of %li (derived from %6.10f)", intValue, longValue, floatValue);
Serial.println(pout);
The length of your line is limited to the size of the pout array (250 chars).
Well, OK, but imagine yourself an Arduino noob. Do you really think that person will understand that code (not just copy and paste)? And, horror, you're still using Serial.print, the very thing we're trying to get away from and back to standard C++. Two statements where one will suffice.
Great video. This library has proven to very useful in my projects. However I have 2 caveats for you viewers using PlaformIO. If one uses PlatformIO library manager to install this library, for some unexplained reason, the folder "Extras" which is referred in the LibPrintf .cpp and .h files, is not installed and therefore the project fails to compile. The solution is to download it from GitHub and manually install it. The second caveat is with the latest version of the library, V1.2.6. Although the library should use the Serial stream as its default, it does not. One has to initialize the printf object with the command "printf_init(Serial) in the setup function. Interestingly these "undocumented features" are not present when one uses the Arduino IDE. Thanks again for your great video.
Excellent update for PlatformIO users, Jacques; thank you for taking the time and trouble to document it all here. 👍 I'm sure there will be many viewers who find this comment (once Google has trawled it) 👌
Many thanks for this update. I am certainly going to take full advantage of 'printf' as you describe it in this video. I have always hated Arduino IDE for NOT including this feature. My last comment: your thumb had an "issue" which I assume hs been 'sorted'. Like the new video format you are using, too.
UPDATE: Using VSC & PlatfromIO, I placed in my project folder's /lib folder, a folder /LibPrintf which has LibPrintf.cpp and LibPrintf.h. AND I had to include the /extras folder, too. So far this is brilliant and you 'prodding' has done the trick. However, when I had PlatformIO download and install the LibPrintf library in my project folder, it couldn't find the /extra folder it said. VERY CURIOUS!
Indeed, printf is very useful.
I use this on PlatformIO all the time but I don't recall the 'extras' folder being an issue (or being present, if it comes to that). I might have installed the library via PlatformIO (ie an entry in platformio.ini), I just can't remember especially as when using ESP32s you don't need this library at all, it's all built in to the framework.
@@RalphBacon All built into the framework. Wait, isn't it using the same Arduino Framework like with Arduino Uno boards?
FYI Ralph PI is declared as a global variable in the Arduino.h header so you can use it, as well as:
#define PI 3.1415926535897932384626433832795
#define HALF_PI 1.5707963267948966192313216916398
#define TWO_PI 6.283185307179586476925286766559
#define DEG_TO_RAD 0.017453292519943295769236907684886
#define RAD_TO_DEG 57.295779513082320876798154814105
Excellent update, thanks Chris!
At 15:24 you say %s is a C++ string but it is a actually a C string.
C++ is happy with string objects and supports c strings only for backward compatibility.
It is stated plainly on the page you referenced "Writes the C string pointed by format to the standard output (stdout)."
Certainly C++ supports the object called 'string' and would be a better (?) way of dealing with character strings. Then again, a null terminated character string (char *) is well understood so is often used too.
Great work Ralph, I have been using printf since your video #168 where you introduced MCUdude minicore. He implements it natively in Serial so you can use Serial.printf(...) . What did you do to your thumb?
Yes, miniCore is great, Andrew, but I suspect that most beginners do not flash their Arduino-type device with a bootloader. Even if they do, they pick the standard board from the dropdown list. So this is an alternative way of reaping the benefits of printf without all the faf. (Technical term).
My thumb? Picture in your mind's eye the delicatessen counter of your favourite supermarket. The area where they have the meat slicing machine. Now picture me in my workshop with a very sharp craft knife taking no precautions whatsoever. Yup. Same result.
Thank you for taking the time and effort to create this tutorial.
Glad it was helpful! Nice to see you again, Terry (I'm looking at your avatar).
@@RalphBacon did you look into the floating ground issue that I mentioned a while ago?
(When two buildings are electrically connected such as your home and the new workshop.)
The new UK standard, Terry, it to NOT join a building's earth with the outbuilding's earth due to various risks (eg cable breaks) and also the potential difference between the two points due to resistance of long(er) cable runs.
My workshop, for example, has an earthing rod buried next to it and to which the earth in my consumer unit is attached. There is no common earth between my house and my workshop.
Ralph S. Bacon, a hoopy frood who knows where his towel is.
I have only one thing to say to you: 42 😁
Best thing before sliced bread? Cornish pasty?
Oh yes! In fact it might be better than sliced bread...
I do MOST of my RUclips viewing on my phone. Some on my laptop. And some on the 50” tv in the lounge.👍
But I must correct you on your hhgttg…. 42 is not ‘the meaning of life’. It’s only a number.
42 is the answer to the great question of life, the universe and everything. The biggest supercomputer ever know, called Deep Thought , was built to calculate the answer to that question. And it came up with the disappointing result of ‘42’. So the planet builders of Magrathea were contracted to build an even more powerful computer, called The Earth, to calculate what the actual question was. Unfortunately, moments before it completed its computation it was unexpectedly demolished to make way for a hyperspace bypass.
I can see you're a fan of HHGthG (aka HG2G, HHGTTG, H2G2, and tHGttG), Jeff, and always know where your towel is. Cool.
@@RalphBacon you, sir, are a hoopy frood.
all that hard work and some space peasants break your hardware :(
good vid, coming back to the atmel 2560 for 1 project after using the esp for years forgot about the printf not being included. Ended up using sprintf as a quick fix
Well, sprintf is included as part of the Arduino framework but, for beginners, error prone as buffers easily overflow. Printf is better a lot of the time IMHO.
Floats, when used properly, are accurate!
Floats can never be truly accurate. It's a bit like saying, Represent 22/7 (an exact figure, of Pi) using a float. Impossible to do!
Thanks Ralph! What program are you using for your serial monitor?
It's called CoolTerm from Roger Maier, who writes freeware:
freeware.the-meiers.org/
But do support him if you find it useful!
Who's not heard of The Hitchhikers Guide To The Galaxy?
When I was on a trip to London in 1999, I managed to get the BBC radio play on cassette tapes :-)
I had failed to find it in Norway, but was happy when I got it :-)
On "cassette tapes" you say, Thornberg? I've heard of those. Weren't they something used before CDs? Except they gave you added hiss for free, and if that wasn't bad enough got tangled up inside your car player on a hot day. 🤦♂️
AH! found a SOLUTION!
Yes a buffering issue.
I can use fflush(stdout); after the printf statements that don't have
on them that works with my ESP32 dev board. reading in C++ reference there is also the option for setvbuf but I don't pretend to understand that one! both methods can cause performance issues but for my simple test sketch fflush is fine!
Hi Ralph, Oddity with the
escape sequence! I'm running a small sketch with a matrix keypad on and ESP32 dev board. keypad all works fine so I had a go with printf (native to ESP32 so no library involved)
My code displays keypress to the console without the
and a counter sends a
after every 5 keypress's. Problem is nothing appears on the serial monitor or cool-term until the 5th keypress. unlike your last example in the video. Seems to be an issue with the native ESP32 implementation not flushing to console output until a
is issued. How do I do that? and ideas?
On an ESP32 it's far better to use log_x where "x" is v(verbose), d(debug), i(information), w(warning) and e(fatal error).
Example: log_v("Reading temperature sensor");
Basically, Espressif have massaged this "log_" macro to use the underlying printf to allow more granular messages, depending on what you have set the Core Debug Level to in Tools>Debug Level (0=no debug, 5=verbose). One beneficial side-effect is that you _don't_ need to append
to every line! Oh, and you get the function from where it was called and the date/time too!!! All good and very useful.
That said, there is nothing wrong with you using the built-in printf, but the Arduino Serial Monitor does expect a line-feed/carriage return (or some combination) before it outputs the data. As I now only have Arduino IDE 2.0.1 installed I can't remember if you can change this on previous 1.x versions.
You could try executing Serial.flush( ) in your sketch to see whether that actually forces it out of the UART.
CoolTerm (freeware serial monitor) allows you to customise this and umpteen other things and it's what I now use (using it right now!). I've liaised with Roger, the author, over the last couple of years and he's done some amazing work including some of my suggestions!
freeware.the-meiers.org/
Check it out!
@@RalphBacon Hi Ralph, Yes I'm on IDE 2.0.1 so same options as you. I do plan to look and the log_ macro further down the line, my project is a IR sender for legacy kit for smart home integration (Alexa!).
At about 12:04 you say that that the value of Pi is "garbled". What actually happened is there are rounding errors. When a number is stored in a variable it is converted to a binary representation of the number. When it is converted back to a decimal string representation for printing there is typically a rounding error. You can get a value closer to what you are expecting if you employ some rounding and truncate off a couple of the least significant digits. There is no floating point unit in a simple microprocessor so floating point operations are much slower than integer operations. This is the more typical reason for avoiding floats where possible.
You are probably aware that my stance on floating point values are that they are the spawn of the Devil. Use integers wherever possible and divide by the appropriate amount (10, 100, 100...) at the point of displaying the value.
@@RalphBacon I heard you say that. Good luck if/when you need trig functions. Are you going to write your own libraries for cos, sin and tan?
If you want Serial.print() to print PI to 8 places use Serial.print(PI,8), the printf library uses too much space (~10K) on a Nano/Uno which is what most beginners will be using. BTW l is a length specifier for printf not a long type it promotes the i or d (signed decimal integer) to the length of a long integer.
Well, I have to disagree about the space constraints. I use it on the Arduino most of my sketches (I hate those multiple Serial.print statements) and I've never had an issue. Perhaps my sketches are particularly small!
@@RalphBacon Serial.print = 4% of space on a nano, and by default - printf() = 38% on a nano (So you MUST set options to reduce footprint)
Another great and very informative video, particularly with your earlier video on saving space with debugging statements.
Glad you enjoyed it!
I tried to use the printf() in combo with the previously suggested DEBUG stuff, like:
#define debug(x) printf(x)
.
.
debug("Distance: %f Duration: %f
", distance, duration);
.
.
and the compiler complained about to many parameters, b/c debug(x) wants one parameter, and I supplied 3, Somehow the full substitution didn't go as planned. Any suggestions?
I'll reply to myself... I tried
#define debug(x,y,z) printf(x,y,z)
and this solved the problem. So will have to use several "#define debug"s if want to print different numbers of specifiers in different parts of sketch.
Hi Ralph, here is a great Arduino function and worthy of a video. I wanted a simple way to generate 100ms and 1sec ticks for program timing, built-in timers can be used but not for any time of day code, as when they over flow and are reset there is no timer function taking place, so they become inaccurate over time. millis() is a none blocking ms counter and counts from reset up to about 50 days before rolling over. Example use, counter = millis() / 100 to get 100 ms or counter = millis() / 1000 for seconds count. Virtually any timer can be defined in code and with my Arduino nano_connect (rpi PICO) it keeps perfect time over many weeks, so negating use of RTC in some instances.
Sounds fine! But Timer0 (used for millis function) must overflow too (after 49 days) so will lose a millisecond or two at that time? I've never really looked at what happens. I would expect the maths to just keep going and the unsigned long just goes from 0xFFFFFFFF to 0x00000000 in one tick of the clock?
I'm also guessing that your formula should read:
millis( ) % 100=0 or
millis( ) %1000=0
to get the modulo of the divisor?
I say that because I use that to flash an LED:
digitalWrite(ledPin, millis( ) % 1000 > 500 ? HIGH : LOW)
You can change the 500 to get a briefer or longer flash every second!
@@RalphBacon Yes there is a short duration overflow at 50 days and reset to 0x000. One question for using non blocking millis()to replace the blocking delay() shown below. How could the code be rewritten to make use of the non blocking delay in millis() and allow other processing to take place while creating a delay in function1 ?
function1
delay()
function1
If you have already done a video for this could you point me to it?
I am fairly noob myself and I am using Serial.print and Serial.println still... To make it fit into a single line of code, I simply use the String() function which in this case would equate to:
Serial.println("Iteration " + String(intValue) + " has a value of " + String(lintValue) + " (Derived from " + String(floatValue) + ")"); I admit it's not as elegant as the printf() function, but it does work in the Tinkercad simulator whilst printf() doesn't.
Well, that's fine if it works for you. ✔
Be warned, however, that using the String function (capital S) in an Arduino environment can make your heap memory look like Swiss cheese (and cause crashes) _and_ all those quoted literals are taking up room in your run time memory (SRAM). Just sayin' 😲
Joemozz, see my comment above, and try the 'Streaming' library. You'll love it.
@@johntoe6127 Very interesting thanks ! I'll look it up for sure.
@@RalphBacon True enough. Thanks for taking the time to respond BTW. It's really nice when you can get an answer back from a knowledgeable person like yourself. Most of the time, youtubers don't even read, let alone respond to such comments... (no beef from me either way though: It's easy to understand why one wouldn't have the time to answer each and every viewer query...)
As for the memory consideration, I'm not worried too much because I generally use the Serial.print statements for debugging only, so these don't end up in the "final" code ... I am interested in the streaming library John Toe mentioned though and will look into it. Cheers!
👍
Thank you for explaining the use of printf, this function seems to have more powerful formatting capabilities than Serial.print, I will try it next time I need to print with an Arduino. However, I disagree with you on two points :
First, I personaly don't like long printf lines (nor any long lines) in code. It is more difficult to understand what happens than with many short lines, and more difficult to modify, should you change someting later on. If one gets too many lines of code, one can always write a new function to move them elsewhere.
Second, I think floating point are marvelous, provided you understand what precision they have. Arduino has only single precision, therfore it is normal that you obtain a 6 to 7 digits accuracy for you PI representation! The error was in your expectation, not in the way the number was represented ;-)
I hear you on your coding preferences, Vincent. You must code the way you want. Remember that the code in this demo was deliberately designed to show the capabilities; I'm not sure I'd right actual code like that either.
But floating point variables are still flawed. No floating point number can be accurately stored in binary format; there will always be errors. Most of the time you don't notice (or don't care) but I've been bitten by them and decided the sensible option is to multiply them up to a whole number, so all the work is done with whole numbers, and then divide back down so the conversion is as accurate it can be. But if floats are working for you please do continue using them!
Thanks for your thoughts and support, appreciated.
Thanks Ralph. I'll be pointing my students to this video. Very well explained!
Thanks, gord, glad it's helpful.
The thing you mentioned about Floats and Doubles is really interesting... How would you go about measuring a simple voltage using an analog pin and not using a Float? Or are Floats only inaccurate when you are storing a number, and then want to recall that number on various occasions? Or have I misunderstood entirely!?
If you measure a voltage using an analog input on a microcontroller, you will get an integer number. On the Arduino, as it's only 10 bits, a number from 0 to 1023 (not too accurate at the top end, I might add).
For the sake of argument, if we _know_ that a reading of 512 is 2.5 volts why would be store that as a float/double when we also _know_ that we will lose accuracy. Better to store that as an integer of 25 (or 250, or 2500 depending on how many decimal places you intend working with) and only put in the decimal point when we need to display it.
If we were to keep that value as a float of 2.5 it might get stored as 2.499999931 so bang goes our accuracy!
Clear as mud now?
@@RalphBacon Thanks very much for taking the time to explain. Yes I understand that now. I watched this video when it came out and when you mentioned not using floats and doubles, it played on my mind ever since!
Thanks Ralph, you explain things really well. I have only just moved to PlatformIO. You seem to have a very clear Serial Monitor. Is this a PlatformIO extension and if so what is it?
The serial monitor I use (when I am not using the built-in one that comes with VSC/PlatformIO) is called CoolTerm: freeware.the-meiers.org/
It's freeware from Roger Meiers but if you use it a lot, do consider a small donation to keep motivated!
Surprisingly useful! Thanks for the tip Ralph 🤘🏻
(Watching on my phone 😉)
So YOU are the person watching my videos on your phone!!! Hang on, what do you mean "surprisingly" useful? Did your Dad put you up to this, Calvin? I'll be having words! Hope everyone is OK back in the Vaterland!
@@RalphBacon ONE of the many phone users 😂 well I've been using serial.print in my code for debugging and it works fine but as you've mentioned it's a lot of stuff to comment/uncomment every now and then 😁
All good over here on my end, might wanna check with dad regarding the house though 😬😅
correct me if I'm wrong. I remember using a command called " Serial.printf(); " which works similar to printf in c. what's so different between that command and this library?
If you know of an Arduino standard Serial.printf library or function do paste the link here, I'd be delighted to see it (but not the one I'm promoting by EmbeddedArtistry).
It's not working on arduino but esp8266 is fine.
I feel lack of response to unasked question: how much does libprintf cost?
I mean - just beggining Serial does cost around 1kb of space. How about this library?
The size is in the read.me page of the GitHub library. You can reduce the footprint by discarding formats you won't use (eg float). And the linker will eliminate as much as it can to keep the size down.
The library adds about 4.25K if you disable long, long ; exponential ; and floats otherwise the whole library adds 10K.
The best thing before sliced bread was and still is oven baked bread.
I've got to agree with you there, oven baked fresh bread is the best. Still warm, so the butter melts on it.
Thanks Ralph, I’m familiar with and have used pointers for a long time in C but haven’t seen references like this before. Are they a C++ only thing?
In C++ we should not really be using pointers at all. References are the C++ equivalent but are much safer. Obvs you _can_ still use pointers but then you are writing C not C++. I fell into this trap just a few weeks ago but have since modified my code.
@@RalphBacon If you use printf you are in fact using pointers. The site you referenced cplusplus has a page for cstdio which is the C library to perform Input/Output operations. It mentions pointers throughout. The format string is a const char * . If you use the %s format specifier than the printf function is expecting a char * type variable. In both C and C++ a char array (of one dimension) is a pointer to the zeroth element of the array. When a C string is passed to printf it is the pointer that is passed.
Another very informative video, as usual. Thanks Ralph!
So nice of you, Arnie, I hope you enjoyed it.
A nice video informative and I can see you are getting fed up with the serial.print function in your previous programming:-) did the lid drop on your Thumb:-)
Yes, you are right! I use _printf_ all the time in my ESP32 work and I use the technique I described in video #224 for Arduino work (other than just for trivial, experimental stuff).
Great walkthrough video as always 👍
Thanks for sharing your experience with all of us 👍😀
Thanks for the visit, Asger, always good to see you here!
Thanks, great tip again. When I try to combine this tip with the previous one on removing the serial.prints with the debugger to speed-up everything there is an issue with the number of arguments that is always different. Is there a workaround for that?
So you're trying to use _printf_ with the previous tip of using precompiler directives so you can switch them off?
OK, David, I specifically did not mention how to do this because it can seem quite daunting. There is a construct that allows an "undefined" number of arguments to a function that you can then retrieve (it's how printf manages to do its stuff).
So your #define of debugf, for example, might look like:
#define debugf(x, ...) printf(x, __VA_ARGS__)
*[Note: that's 2 underscores before and after VA_ARGS]*
Now you can use:
debugf("A number %d, and string %s", 5, "hello");
And they will disappear if you define debugf as "nothing", just as before.
If you need a better example email me and I'll send you my example code.
@@RalphBacon This worked perfect. Thank you.
Another great video!!🤣 In the Specify_print_class the statement printf_init(Serial1). How or can you assign a second serial port?
Some modules have more than one serial port (the Mega 2560, for example).
Otherwise you could use a Software Serial (it's a library) port too, but then you would need a USB-to-Serial adapter to connect to (eg an FTDI or similar) but that should be OK.
Like your style. keep em coming ☺
Thank you! Will do!
Very good explanation. Thank you. I just start using it and for me it seems better than serial.print. Btw, I watched using a cell phone 😀
Another cell phone viewer! I will definitely have to take this into account in future videos so it is watchable on such a small device. I had no idea. I'm glad you think printf is better than Serial.print, too.
how about a video about not using floats? I made a solar power monitoring system and needed to use floats for Wh calculations. Is it easier to not use floats?
Not _easier_ Pawel, just more accurate. Try it yourself. Multiply a few floating point numbers together then divide by another float. Now do the same with those same numbers multiplied up into whole numbers (eg x10, x100 or x1000). Only divide at the very end of the calculation to get a floating point result. You'll see a different number. Whether that is important to you in your project only you will know.
@@RalphBacon : for my garden temperature, I use deg F and ints.
67.45 as a float becomes 6745 as an int.
saves space.
these "Advanced" / alternative coding videos are a great idea any chance of doing them on a regular basis? maybe drop them in inbetween projects?
We shall see, Gary, we shall see. But this is not really advanced. Any beginner who can write *Serial.print* can write *printf* and get added value. But I understand your point!
@@RalphBacon I would say advanced in the arduino world! maybe alternate or optimized code then for c++ programmers😂
This works perfectly fine w/o external libs (real copy-paste from my TM1640 display for lab PSU):
float voltage, current, power;
static char sprintfBuffer[16];
static char voltageChar[7], currentChar[7], powerChar[8];
dtostrf(voltage, 6, 3, voltageChar);
dtostrf(current, 6, 3, currentChar);
dtostrf(power, 7, 3, powerChar);
sprintf(sprintfBuffer, "%s%s%s", voltageChar, currentChar, powerChar);
Serial.println(sprintfBuffer);
display.println(sprintfBuffer);
How spooky you are testing that using a TM1637, Leeroy, as I'm using that in my Smart Monitor Controller project - but I'm using a library. Perhaps I won't need one now?
@@RalphBacon w/o external libs for formatting that is. sprintf() and dtostrf() are standard Cpp functions that all work with Arduino framework (where it does not break them specifically).
Ofc, for TM1640 and INA measuring module I am reusing public code because there it is worth a lot more effort, but you still could access registers directly for sure.
sprintf works OK w/o a library but only supports int variables (at least in AVR)
dtostrf will format all variable types and is great for displays but again multiple lines of code to create the str.
@@blic-sx9ix "multiple lines of code to create the str" true, but that's a norm in C, it's a low level lang after all. I just do not consider reaching for custom lib and looking up its docs for specific use case just to avoid couple lines of code for formatting an easier way, than properly learning your standard libs. Yeah, strings are not as easy in C as in other languages, but that is a quirk of lang itself.
@@Mr.Leeroy I agree with you a few lines of code is not a bad thing. Just that Ralph's video was about reducing multi-line Serial.prints. The alternative is the library mentioned that comes with 5k+ memory footprint.
Which is more performant in terns of ram/stack space, program space and execution speed in very small (tiny) memory footprint Arduinos?
In all cases, SRAM runs fastest, program memory (flash) runs more slowly and EEPROM flash, if present, (probably) runs the slowest. Heap/stack is always allocated from SRAM but the program runs from flash. You can put (larger) variables in program flash too (use PROGMEM) which saves valuable and limited SRAM.
Dear Ralph, thank you for your explanation of printf(). However, I do not understand how you derived 1000000 from 3.1415926 after 42 iterations.
just a demo, not a real calculation
Special maths, John, obviously. And partly down to the Hitchhiker's Improbability Drive too.
ruclips.net/video/zjbtZ4NgtdA/видео.html
@@RalphBacon Whoever invented Special Maths has a lot to answer for. IMHO they had Special Needs (no offence intended to anyone I may've insulted).
@@RalphBacon Oh dear I will have to search RUclips now to learn how to do special maths!
(disclaimert: I know you are trying not to lose beginners here, but I can't resist) With the advent of calculators we've lost the understanding of significant digits. When people calculated by hand they knew better and stopped when more didgits didn't make the answer better. In this case pi would be 3, because while 42 has 2 significant digits, 1000000 only has one. I may have that slightly wrong as I got a poor grade on the significant digits section in maths class. plus among my friends the more digits of pi you can recite the greater your status. I guess I am saying float and double have their place. 11/3
I reckon float and double would be more useful in computers if their precision was not in doubt.
As my demo showed, even a number typed in directly had been compromised, simply because they cannot be accurately represented in decimal format . That said, my temperature sensor returns a float with 4 significant figures (mostly) and two digits of precision and it seems to be good.
Now I'm off for some apple pi. 3 pieces. Give or take.
Hey Ralph, Not another of them dang coding vids.. Hehe all good, i enjoy them all.
Come on, Mike, bite the bullet and start coding again!
Thank you, very helpful.
You're welcome!
Nice, I am so going to try this.
Please do!
Hey Ralph, nice thumb. Please do tell us how we can get one of our own.
By the way, another excellent video. Thanks.
To get a thumb like mine you need three things:
1) a thumb
2) a very sharp craft knife
3) stupidity
I'll say no more. It's almost healed now, a couple more days and the dressings will stay off, I reckon.
Can you use FlashStringHelper with printf? Otherwise I could see sram usage going up if you can't put your strings into flash using FSH.
I tried to use the "F" with printf on an Arduino and got the following error message:
⚠ argument of type "const __FlashStringHelper *" is incompatible with parameter of type "const char *"
So I guess the answer is No. And you probably won't want to anyway because strings in the F macro are const and cannot be changed; the printf macro injects run-time values.
At best you could store the F strings (with placeholders) and then perhaps use those as part of the printf statement, together with the variables. Not sure that will work though. My quick play-around didn't elicit any success. 😥
@@RalphBacon Hmm. Do you think they could be stored as variables in PROGMEM and then called in to a printf with a pointer, maybe? I guess for any long text you could still use serial.print was just thinking that the F() macro for FSH does save space when you need longer strings. Thanks for having a look!
Certainly storing (and then retrieving) strings in PROGMEM that contain all the necessary placeholders would work, and might even be required on smaller processors. But it also means that your messages are predefined, no random off-the-cuff messages.
Incidentally I found a page that seemed to prove this all worked with the standard snprintf and snprintf_P but I couldn't get it to work with PROGMEM - it compiled but nothing was displayed at all!
Any use of this to impose output formatting to LCD?
Indeed.
You need to print the formatted value to a buffer string (char) and then print that formatted string to the LCD.
char buffer[100]; // Ensure it is big enough!
sprintf("This is a decimal %02d value", myIntVariable);
lcd.setCursor (col, row);
lcd.print(buffer);
Make sense?
that library alone eats up 38% of my nano program space
It depends on which functions you are calling. I think you will be surprised at how little it actually uses in a final project. On an ESP8266/ESP32 or even STM32 with much more memory it no longer matters.
Uses about 9K of my nano
I like the tidyness of printf over serial print, but Im not happy about giving up 1/3 of my memory for it. According to the library readme file, if memory footprint is critical, you can disable features; "For Arduino IDE, the flags need to be added to the compiler.extra_flags property in platform.txt or platform.local.txt".
I found the file, but its not obvious in what way I need to edit it to disable the features Im not using or how much memory Ill be able to save. Anyone able to help?
I don't know the answer, but it might not be needed if your sketch is not already pushing the memory limits. I find I rarely use so much memory that I can't also use printf.
Can you not use the standard (cstdio) C++ libraries in Arduino?
video is targeted at arduino beginners
You can import the library below to get more (most) of the standard C++ functionality but how good/standard it is I don't know. It hasn't been touched for 10 years:
github.com/maniacbug/StandardCplusplus
Is the %i %li and %f order of declarations critical. Like if you had %i and %i
The order of the placeholders in each printf statement simply determines where they will appear in the final output. Look at this example where we might have two decimal values, A and B:
printf("This is value A %d and this is value B %d", A, B);
Note that the final A, B are placed into the string in that order. Is this what you mean?
maybe the trick of using an empty comment block at the end of a code block works in arduino,
example end with a pure /* */ and then when you need to block the the code above it just need to add a /* at the beginning of your code block, the whole /* ...... /* */ is now a comment, the second /* is ignored.
no need to delete the /* */ each time.
Is this a suggestion to comment out debugging statements, Jyv? I reckon we could easily tweak the precompiler trick I showed a few weeks ago to achieve the same functionality.
On the ESP32 this is already built in by using log_x where x is e, w, d, v and controlled by the debugging level declaration.
I guess I missed the point of the video this relates to. But instead of faffing around with / and * you could also use a proper IDE or source code editor (Arduino IDE does not qualify for that, well at least it didn't last time I used it), which will provide a simple hotkey to (un)comment your current selection.
@@superdau [CTRL][/]
Uhm, working with Perl for so long time and entering Arduino world made me feel that Arduino is so neutered from usual tools that i used to use xD
I understand lack of foreach() as Arduino "flavor" of C bases on, well, C. But ommiting something as basic, as taken for granted, as (s)printf gave me that feeling that i probably chose wrong hobby xD
I am still a begginer on Arduino, yet i have this feeling that next wall i will painfully hit will be strftime or something similary "granted" by standard library ;p
I think once you know one language others come much more quickly. But you do end up writing C++ either in a JavaScript page or C# page or something similar (not that I have _ever_ done that). Spending 15 minutes wondering why some syntax isn't being accepted then discovering you should be writing Java not C++ is always amusing (not).
@@RalphBacon oh my, you are so right :D. i can't even count how much i ran into this when working with "inline XYZ" inside program written in ABC :P
OK great to not use floats, providing they are constant. If you consider something like a macro rail that mathmatically keeps track of the camera mathmatically. To move 4 mm 12800 steps need to be taken. I want the system to move to 12.895mm, and then take a shot every 0.02mm in the opostire direction for 4.68mm. So with that a step size of 0.0003125mm calculate the number of steps neede to get to the starting postion, the number between shots and the number of steps for the diseried 4.68mm. Or I could put in the total distance i want it to cover and the number of shot and have it calulate everything else. The further to to that the system hase to do thing like calculate depth of feild, feild size and image overlap via a percentage. These are going to making very heavy use of floats as it's hard to find a way of multiplying (x10,000,000 would be the smallest possible solution -this wil cause issue when counting longer distances) out for every possible iteration and would most likely take up as much time and space as using an exteranl FPU (better yet use a chip that has one).
Floating point rounding error is very well documented and every digital adding device has the same issue. I am 100% certian that the error that you get from it isn't going to effect any arduino project enough for it to be an issue - it's why they don't use them for protien folding! Honestly saying they are inacurate is like saying you don't trust your phone, your calcuator, your car, your microwave, your camera... you get the picture. It's not an issue until you get to a serious level of computing, way, way beyound what anyone will be doing with something as small as an arduino or other microcotrollers.
It is also impossible to store the exact number of pi - it's an irrational number as we have not found an end to it. A float to 15dp is way more than good enough. Then the number such as 10/3, will never be accurate as we, as humans, will just round it at some point. It has no end, it repeats indefinitely.
The only issue is the understanding of what is required, and what is not. Accuracy to 30dp, not normally required, for most cases 4-8dp is enough and arduino software floating point is capable of that
Also
Serail.print(float, Number of decimal places); //this is day one arduino stuff
If you want to use floats, especially on an Arduino that has no FP co-processor, be my guest! 😁
On an ESP32 they work better as they are built to use floats.
Or, just create a float in a sketch, type in a decimal value and see what you get back out of the Arduino. If it works (well enough) for your case then by all means use them.
But I'll stick to integers and divide by the relevant factor just before printing, as that way I know I'll be getting the most accurate numbers the processor can provide. Just sayin'!
@@RalphBacon great until multiplying it exceeds the interger limit. Tell me how do you multiply out 0.0003125 so it's compatible with the 480,000. Yeah multiply by 10,000,000 which then exceeded the storage of an integer. Honestly I work in metrology and can assure you the floating point rounding error is no where near as much of an issue as you think. Also esp isn't the only system with an fpu! It's only the discontinued teensys that don't example. Or you could use an fpu co-processor. Used ones with a picaxe may years ago. You do seem to have a narrow way of thinking, just sayin.
thanks for this very helpful video, test files and all other information stuff
You are welcome!
max baud rate for arduino not 9600 ? thx
No, 115200 is safe enough for a 16MHz Arduino.
It would be useful if I can 1-shot snprintf the names of enums too like with booleans and %s
Stackoverflow had several threads on this topic but the solutions were quite involved. Have a search and see whether any of the proposed solutions (not standard C++ one, I'm afraid) meets you needs.
@@RalphBacon Yeah I have seen some threads. Tho come to think of it, it's not too hard to stuff the enum name into a corresponding char array's index.
please try the Ayu theme for VC
Do you consider it a good alternative? Narrow fonts are not that easy to read (especially when doing demos). But apart from dark, light and mirage (the one I find easiest to read) what else does it give us? A choice, I suppose!
Or else: sprintf to a character array, then println that array. No xtra library needed.
Certainly another, albeit less straightforward way, of doing it 😉
Very informative video Sir
Glad you liked it Muhammad, nice to see you here.
42 isn't the meaning of life, the universe and everything. It's what you get when you multiply 6 by 9.
Well, not strictly true, Michael.
42 was indeed the answer that Deep Though gave (after some 7.5 million years) but indicated the question had not been properly understood by original programmers.
So it built the next generation of computer (known as Earth) to answer the question, which it failed to do as we were obliterated to make way for a hyperspace by-pass just a few minutes before the answer was due.
Arthur Dent, however, took some random scrabble letters to get the 42 answer to 6 times 9.
Which it is, if you are working in base 13. Simples! 🤣
Very useful stuff....cheers.
Glad it was helpful!
👍 great video
Thanks for the visit, Adam, glad you liked it.
That ("\") is not a slash. It's a backslash.
This is a slash: /
Agreed, but in what context are you saying this, Martin?
Meanwhile, the entire audio software industry uses floats just fine.
Well, Chris, maybe the entire audio industry doesn't use values of a different order of magnitude, or have very low value figures or they don't add up thousands of them... all of which will cause loss of precision, sometimes catastrophically. (You can even prove this in Excel).
Or maybe the loss of precision is not that important in audio work, although something tells me that probably isn't the case.
As you've made the effort to comment, I've dug out some further reading for you that explains the (potential) problems well:
hownot2code.com/2017/06/09/float-or-double/
What do you think of that (5-minute read) article?
It's all -1.0 to 1.0. Wasn't doubting you.
Guys: the main issue with floats and loss of precision is detailed in Computerphiles float video. ruclips.net/video/PZRI1IfStY0/видео.html
"once you'll do you'll never forget to do it" (about putting a
).
Yea, also Perl taught me that every print(f) end with
, also end of sentence is not a dot but semicolon;
;)
It's even funnier using the "log_x( )" on the ESP32. The log_x (where x is either e(rrors) w(arnings) d(ebugging) or v(erbose) which basically calls printf, automatically adds in a "
" so you have to remember _not_ to include that! (OK, you can switch that off, but...)
8-bit Mega's are great for starting out, but there are the Teensy series that use Cortex CPU's that will blow the Mega out of the water With FLOPS. Serial.print(floatValue, 8); should print the float value to 7 digits including the whole number. What would be really interesting is to get printf or print to output as scientific exponential eg: 4.7e-6
But you're not crazy enough to do astrophysics on a 8-bit micro with no FPU clocked at a sloww 16MHz struggling doing single precision float math at ~78uS/op. On any Mega (328,2560) a double is 4 bytes as is a long and is at best 2x slower than int (2bytes) math.
Raspberry Pi's can do double precision, have division units, and are clocked from 500..2000+MHz. So C language operations are a LOT faster on something like a Pi or Teensy (also clocked from 60..600MHz).
The ESP32 (240Mhz, dual core) is also quite nifty and can do a lot of work. Compared to the Arduino's ATMega328P... well, there is no comparison. But some complain about the ESP32's FP performance:
blog.classycode.com/esp32-floating-point-performance-6e9f6f567a69
Using printf since the 1980s....
Just not on the Arduino, hey, Doc?
😍😍😍😄
Hey, Yogesh, nice to see you here!
i haven't got it, by the end which library should i download to use printf with arduino ide2.0?^??
in manage library, LibPrintf
That's the one.
unfortunately my printf doesn't show a damn on the monitor otherwise Serial.print makes it fine, Libprintf is installed, and #include
Use the demo sketch I (probably) included in the GitHub, it deffo works just fine.