Serial.printf

Hi guys,

I've been looking at implement Serial.printf for the STM32 ARM platform, but I'm a bit stuck on how to implement a FILE stream

I can see how to add a printf to the print class like this

int Print::printf (FILE *__restrict __stream,__const char *__restrict __format, ...)
 {

     int ret_status = 0;


     va_list args;
     va_start(args,__format);
     ret_status = vfprintf(__stream, __format, args);
     va_end(args);
     return ret_status;
 }

However I can see that I need to implement a FILE stream which would need to be either passed in, actually initiated as part of the print class its self, perhaps lazy instantiation.

Which is where i get stuck.

I've tried to find out what FILE actually is, and it seems to be a struct (which doesn't make a lot of sense).

Can anyone point me in the right direction of how I could implement the most basic stream that would give enough functionality for vfprintf to use?

BTW. I know that this takes a lot of program memory.
Just compiling in the code above has already added about 21k, but the device has 100k program memory, so its not too much of an issue as long as it would only get compiled in when it Serial.printf was called.

Thanks

Roger

What's the FILE stream got to do with it? Can't you just derive from Print?

Hi Nick,

I may be wrong...

But as far as I can see, to implement printf as an addition to what Serial can do, I need to add the printf method to the the Print class.
Yes. I could add it to Hardware serial in my case,as

class HardwareSerial : public Print {

But it seems to make more sense to add it to all the other print methods.

i.e This is what I've already done, but only half completed.

As printf gets passed var args, I need to pass these to a function that can handle a var args list, and the only function that seems to be available do to this is vfprintf

However vfprint needs to also be passed a stream which appears to be a class with functions like fputc then inside fput c I need to put the actual code that writes to the serial port

e.g. calls

void HardwareSerial::write(unsigned char ch) {
    usart_putc(this->usart_device, ch);
}

Or am I over complicating things?

I don't want to use sprintf because I'd need to allocate a buffer inside print, and I'd have no way to know how big the buffer needed to be

The stream method seems the better route, as its going to require less allocation of buffers as far as I can tell. I did find something on an ARM site about needing to implement putc etc but unfortunately I can't find a link to it again.

Edit. Pretty dismal weather isn't it :wink:

I've been doing some looking into this and am not sure it is going to be easy. You could of course do a sprintf into a temporary buffer, and then send that to serial, but you would need to have a large enough buffer for whatever-it-is you are trying to print.

Something like this could easily overflow virtually any reasonable buffer size:

Serial.printf ("%99.99f %150s %150s", 42.42, "Hello", "world");

There is a discussion here: http://playground.arduino.cc/Main/Printf

Example code:

// we need fundamental FILE definitions and printf declarations
#include <stdio.h>

// create a FILE structure to reference our UART output function

static FILE uartout = {0} ;

// create a output function
// This works because Serial.write, although of
// type virtual, already exists.
static int uart_putchar (char c, FILE *stream)
{
    Serial.write(c) ;
    return 0 ;
}

void setup(void)
{
   // Start the UART
   Serial.begin(115200) ;

   // fill in the UART file descriptor with pointer to writer.
   fdev_setup_stream (&uartout, uart_putchar, NULL, _FDEV_SETUP_WRITE);

   // The uart is the standard output device STDOUT.
   stdout = &uartout ;
}

void loop(void)
{
    float seconds ;

    // wait 1000 milliseconds
    delay(1000) ;

    // calculate seconds as a floating point value
    seconds = (float) millis() /1000.0 ;

    // report seconds since starting
    printf("Alive %.3f sec\n", seconds ) ;

/*
    // without printf(), you would do this:
    Serial.print("Alive ") ;
    Serial.print(seconds,3) ;
    Serial.print("sec") ;
*/

#if 0
    // you can explicitly use a FILE structure like this:
    fprintf( &uartout, "Alive %.3f sec", seconds ) ;
#endif
}

That solves your problem of implementing fputc, and that compiles OK. However I have to warn you that this test prints:

Alive ? sec
Alive ? sec
Alive ? sec
Alive ? sec
Alive ? sec

So it appears that printf does not support printing floats any more than sprintf does.

I should draw your attention to: Streaming | Arduiniana

You might find that a lot easier to use.

Edit. Pretty dismal weather isn't it :wink:

Yes, pretty cool today. :slight_smile:

Floating point implementation is too large and for that reason is disabled (minimized) by default. Read the stdio.h for printf functionality and how to enable full floating point conversion.