Go Down

Topic: Using stdin, stdout, printf, scanf, etc (Read 11528 times) previous topic - next topic

WizenedEE

Aug 28, 2012, 10:23 am Last Edit: Aug 29, 2012, 12:04 am by WizenedEE Reason: 1
When people either get fed up with using a series of Serial.print()s or want to do things the Print library cannot (like constant width numbers), common advice on the forums is to use sprintf to print to a buffer and then use Serial.print(buf). However, there are several things wrong with that. The first is that it's kind of annoying -- it takes at least three lines to just print out one line. The second, and more important problem, is that you need to anticipate the largest size print you'll need (easily possible, but again annoying) and have that much free RAM (which may be impossible). However, there is a way around it -- by using printf directly and having it print one character at a time to the Serial stream.

This page: http://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html is where I found most of the information needed to do this. It isn't too hard to set this up for your own sketch:
Code: [Select]

// Function that printf and related will use to print
int serial_putchar(char c, FILE* f) {
   if (c == '\n') serial_putchar('\r', f);
   return Serial.write(c) == 1? 0 : 1;
}

FILE serial_stdout;

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

   // Set up stdout
   fdev_setup_stream(&serial_stdout, serial_putchar, NULL, _FDEV_SETUP_WRITE);
   stdout = &serial_stdout;
   
   printf("My favorite number is %6d!", 12);
}

void loop() {
   
}


However, I then thought about making this so that it would be easier to use. I tried two things: the first was modifying wiring.c to add the setting up to the init() function. This ended up working (although I had to compile wiring.c as a c++ file: the result is here), and I think it's not unreasonable to suggest that this change is incorporated into the main IDE (thoughts?) However, I then thought about making it into a library, and with a little trickery I managed to get it to work (although I think there's more overhead than necessary). The link to the library is here: https://github.com/WizenedEE/arduino/tree/master/libraries/stdinout and here's a sketch where I use both printf and scanf heavily (it controls a robot with a PID loop): https://github.com/WizenedEE/arduino/blob/master/rover5/PIDTest/PIDTest.ino

When using the library, remember that the %f flag doesn't work unless you add some linker options, described on the nongnu page.

Any suggestions on how to call the setup_stdin_stdout() without user interaction would be nice. The method I use is described in Thinking in C++ volume 1, chapter 10. I also tried putting the function in the .init section, but it didn't appear to work (stdin and stdout were not set up)
Code: [Select]

void setup_stdin_stdout() __attribute__ ((section (".init8")));

I tried making a class with the constructor calling the function and then making an instance local to the .cpp file, but that didn't appear to call it either (and it's hard to debug because Serial isn't setup until the setup() function is called)
Code: [Select]

struct blah {
   blah() { setup_stdin_stdout(); }
};
static blah myblah;


There, I hoped that helped anyone wanting to use printf, will influence a future version of the IDE, and will get me some help on how to make the library. Thanks for reading.

rogerClark

Did you ever make any progress with this?

I'm thinking about doing something similar for the STM32 board using the Arduino 1.5.x IDE

However I was going to make a new method of Serial.printf

I was stumped about how you configure a stream to output via a specific function, but it looks like
the code I need in in your post i.e

fdev_setup_stream(&serial_stdout, serial_putchar, NULL, _FDEV_SETUP_WRITE);

So I'll give it a whirl

Thanks

Roger
Freelance developer and IT consultant
www.rogerclark.net

Odeyin

Here is a good library for using STDIN, STDOUT:

  https://github.com/krupski/Stdinout

Camel

#3
Jan 01, 2016, 07:43 am Last Edit: Jan 01, 2016, 08:01 am by Camel
There's a couple of great little C++ methods for printing without wanting to hang yourself. One is variadic templates
Code: [Select]


#include <type_traits>
#include <iostream>
#include <etk/etk.h>

void put(char c) //a pretend serial port
{
 cout << c;
}

class HardwareSerial
{
public:

 void print(const char* cstr)
 {
 while(*cstr != '\0')
 put(*cstr++);
 }
 
 template<typename T> void print(T v)
 {
 char buf[20];
 etk::Rope rope(buf, 20);
 rope << v;
 for(uint32_t i = 0; i < rope.length(); i++)
 put(buf[i]);
 }

 template<typename T, typename... Args> void print(T first, Args... args)
 {
 print(first);
 print(args...);
 }
};

HardwareSerial Serial;

int main()
{
 Serial.print("Hello world ", 56, ' ', 53.43);
}




The other is overloading the chomp operator

Code: [Select]

#include <type_traits>
#include <iostream>
#include <etk/etk.h>

void put(char c) //a pretend serial port
{
 cout << c;
}

class HardwareSerial
{
public:
 HardwareSerial& operator << (const char* s)
 {
 uint32_t l = etk::Rope::c_strlen(s, 1024);
 for(uint32_t i = 0; i < l; i++)
 put(s[i]);
 return *this;
 }
 
 template<typename T> HardwareSerial& operator << (T s)
 {
 char buf[20];
 etk::Rope rope(buf, 20);
 rope << s;
 for(uint32_t i = 0; i < rope.length(); i++)
 put(buf[i]);
 return *this;
 }
 
};

HardwareSerial Serial;

int main()
{
 Serial << "Hello world " << 56 << ' ' << 53.43; //chomp chomp chomp
}


Both are a lot more modern and user friendly (AND SAFER) than sprintf. Variadic templates are the most intuitive IMO, but are only available with C++11 which the AVR compiler doesn't support. g++-arm-none, on the other hand, is actually not junk and does C++14 . AFAIK all methods will require a small buffer when putting ints and floats to a stream.

pYro_65

@camel, you might be interested in my PrintEx library, it contains both these methods for use with any Print/Stream object.

Also there is no need for any buffers with my methods.

https://github.com/Chris--A/PrintEx#printex-library-for-arduino-

C++11 has been enabled since IDE 1.6.6 and can be enabled in 1.5.7 and above.
https://forum.arduino.cc/index.php?action=dlattach;topic=327736.0;attach=128670 New EEPROM library released

Camel

@camel, you might be interested in my PrintEx library, it contains both these methods for use with any Print/Stream object.

Also there is no need for any buffers with my methods.

https://github.com/Chris--A/PrintEx#printex-library-for-arduino-

C++11 has been enabled since IDE 1.6.6 and can be enabled in 1.5.7 and above.

Now that is brilliant.

I've got to ask, how do you get away with printing ints and floats to streams without a temporary buffer?

pYro_65

#6
Jan 01, 2016, 09:26 am Last Edit: Jan 01, 2016, 09:26 am by pYro_65
All the methods use the built in conversion functions (in the Arduino Print library). They are printed directly to the stream/output.

The methods are here.

The manipulators build up a proxy object which is used to store the formatting features. Due to the manipulators being types, they are all compile time constants. The stream functionality is optimized simply to xx.print(...) calls.

I have had a peek at your StaticString lib and it is something I was thinking of replicating a while ago. However PrintEx contains a string which can be printed or streamed to (different take on a string).

E.g.
Code: [Select]

  char buffer[100];
 
  //SRAM printable.
  GString str = buffer;
 
  str.println( "Hi" );
 
  str << "A hex value:" << ios::hex << 12345 << ios::endl;
 
  //EEPROM printable.
  EString estr = 0;
 
  estr << "My EE string" << 12345;


I have only just realized, I have not attached the stream in interface to E/G/PString. I'll do that now.
https://forum.arduino.cc/index.php?action=dlattach;topic=327736.0;attach=128670 New EEPROM library released

Camel

That's interesting, your GString is similar in function to my Rope class only Rope doesn't have manipulators like GString. It's a pretty good way to handle strings IMO. Does GString know where the end of the buffer is? My biggest gripe against C string functions is that they will happily chew up memory they shouldn't.

pYro_65

No, it'll happily overrun the buffer. Its entirely up to the user to ensure they provide the correct amount of storage.

I've thought about a 'safe' version however I haven't seen really any circumstances where code can't be factored to appropriately provide initial storage (embedded systems anyway). I thought of adding debug error handling which would provide errors on overrun allowing a user to change their buffers. Then use the more efficient method in release (no error checking).
https://forum.arduino.cc/index.php?action=dlattach;topic=327736.0;attach=128670 New EEPROM library released

Camel

Yeah, I guess it's generally not too hard to make buffers big enough. It's mostly things like sprintf(buf, "%s", a_string_of_unknown_length_that_possibly_isnt_null_terminated); that get you - and there's a lot of that posted on these forums.

I'll look over your code over the next couple of days, hopefully I'll learn more about manipulators.

pYro_65

The streaming functionality in PrintEx is my own design. The standard implementation most probably does it differently, so if you have questions just ask.

Most of the features in PrintEx are implemented as 'plugin' modules using a concept called CRTP (curiously recurring template pattern).
https://forum.arduino.cc/index.php?action=dlattach;topic=327736.0;attach=128670 New EEPROM library released

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy