10 years ago.

Best way to override error(...) for production firmware?

Hey guys. I'm working on a project will will hopefully make it's way into production at some point, but I'm hitting a roadblock with trying to override the built in error(...) definition. Since this will be a production device, spitting stuff out the UART is obviously not going to work (and will probably screw things up since I'm re-using the UART pins for other things), so I want to provide a function that will save the error string to EEPROM and reset the device. Then, a desktop tool will read that error string and phone it home later on. I've been able to override mbed_die() since it's a weak symbol, so I can reset the device, I just can't save the error first.

1 Answer

10 years ago.

You can of course just change the mbed-src lib. Or make sure error is never called.

But error is:

    #define error(...) (fprintf(stderr, __VA_ARGS__), exit(1))

So you can 'simply' redirect stderr to another location. You can make a class with the Stream class as parent, and you need to give it a name in its argument field, then you can use reopen to redirect the stderr to this class. Random lib which implements this: http://mbed.org/users/chris/code/C12832/file/7de323fa46fe/TextDisplay.cpp.

Either you can make that class simply throw it all away, or write it for example to your EEPROM. Alternatively you can also make a FATFileSystem and redirect the stream to a file on there. But that has more overhead (you can put most of it just in your flash memory, but still), and I think also harder to implement.

Accepted Answer

Thanks, but I'd like to avoid modifying the mbed-src library since that makes updating it more difficult. And the mbed-rtos library already makes calls to error(...), so I have no choice but to try to use it.

I tried creating an ErrorStream class to redirect stderr, but it doesn't appear to do anything but lock up. I've attached the code:

ErrorStream.h"

#ifndef ERROR_STREAM_H
#define ERROR_STREAM_H

#include "mbed.h"

/** ErrorStream class.
 *  Used to redirect error text to the EEPROM.
 */
class ErrorStream : public Stream
{
public:
    /** Create a new ErrorStream object, and redirect stderr to it
     */
    ErrorStream();

protected:
    virtual int _getc();
    virtual int _putc(int value);
};

#endif

ErrorStream.cpp

#include "ErrorStream.h"

ErrorStream::ErrorStream() : Stream("error")
{
    //Try to redirect stderr to us
    if (freopen("/error", "w", stderr) == NULL) {
        //Failed, should not happen
        mbed_die();
    }

    //Set up the stream buffer
    setvbuf(stderr, NULL, _IONBF, 1);
}

int ErrorStream::_getc()
{
    return -1;
}

int ErrorStream::_putc(int value)
{
    /*if (value == '\n') {
        _column = 0;
        _row++;
        if(_row >= rows()) {
            _row = 0;
        }
    } else {
        character(_column, _row, value);
        _column++;
        if(_column >= columns()) {
            _column = 0;
            _row++;
            if(_row >= rows()) {
                _row = 0;
            }
        }
    }*/

    printf("Got %i\n", value);

    return value;
}
posted by Neil Thiessen 28 Mar 2014

Heh one of those small errors that can irritate you for a long time. Had this one in a similar case a bit ago, still didn't had it immediatly.

The issue is printf: ErrorStream inherits from Stream, which has a printf. So there is ambiguity: Should it take the global printf, or the printf belonging to ErrorStream's parent, and it picks that one (so: this->printf). Which then shows up at _putc. Which call this->printf, etc.

Replace it with std::printf and it works fine :).

For your homework, find out what the error message was :P

Got 65
Got 97
Got 97
Got 97
Got 97
Got 104
Got 32
Got 116
Got 104
Got 101
Got 121
Got 32
Got 97
Got 114
Got 101
Got 32
Got 99
Got 111
Got 109
Got 105
Got 110
Got 103
Got 13
Got 10
posted by Erik - 28 Mar 2014

*face palm* of course... It works now, thanks!

P.S. It says "Aaaaah they are coming" :P

posted by Neil Thiessen 28 Mar 2014

Might have spoken too soon, I just realized DEVICE_STDIO_MESSAGES is disabled on the LPC11U35 (for good reason, there's no interface chip to redirect it to the host), so fprintf() will never be called making the stderr redirect pointless. I'm going to start a discussion regarding this, clearly the best plan of attack is to make error() weakly linked like mbed_die().

posted by Neil Thiessen 28 Mar 2014

Well you didn't tell me that you used that one :P.

Can't you simply enable that from your program? Then fprintf will be called again, and since it is redirected anyway it doesn't matter that it has no UART. Not 100% sure it works that simply though :).

I do agree it would be nice to have it a weak function.

posted by Erik - 28 Mar 2014

Trouble is, I'm not 100% sure when the UART gets configured when DEVICE_STDIO_MESSAGES is enabled, so it's probably safest to leave it off. Maybe I'll submit a pull request for a weak function...

posted by Neil Thiessen 31 Mar 2014

Just submit a pull request, you can read about it here.

posted by Neil Thiessen 31 Mar 2014