Go Down

Topic: Using stdin, stdout, printf, scanf, etc (Read 6 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.

Go Up