Setting mbed_override_console to target a file on a block device

Hi,

I am trying to point printf to a File on a block device.

CustomSPIFBlockDevice bd(PTD2, PTD3, PTD1, PTD0);
LittleFileSystem fs("fs", &bd);
int err = fs.mount(&bd);

File logFile;
int err2 = logFile.open(&fs,FULL_LOG_FILE_PATH,O_RDWR|O_APPEND);

FileHandle *mbed::mbed_override_console(int)
{

    // return &logFile;  //Instantly crashes
    
    static BufferedSerial serial(PTC4, PTC3);  //Works
    return &serial;
}

In the above code, I can map the BufferedSerial FilePointer to printf just fine, but when I attempt to to instead return my FilePointer to my log file, it crashes immediatly (and since my printf is remapped, I can’t see the output).

What is the correct way to do this? Per the mbed docs, this should work.

Hello Kyle,

The code snippet above represents only variable declaration + initialization and function definition. It does not define the sequence of program execution (apart of the code inside the function’s body). So when you have

...
File logFile;
int err2 = logFile.open(&fs,FULL_LOG_FILE_PATH,O_RDWR|O_APPEND);

FileHandle *mbed::mbed_override_console(int)
{
    return &logFile;
}
...

it does not mean that the logFile is already opened when the mbed::mbed_override_console function is getting called in the program. In fact, the compilation of the following code results in the same binary as the one above:

...
File logFile;

FileHandle *mbed::mbed_override_console(int)
{
    return &logFile;
}

int err2 = logFile.open(&fs,FULL_LOG_FILE_PATH,O_RDWR|O_APPEND);
...

To make sure that the logFile is already opened when the mbed::mbed_override_console function is getting called move the related stuff inside the function’s body.
For example:

CustomSPIFBlockDevice *bd;
LittleFileSystem *fs;
File logFile;

FileHandle *mbed::mbed_override_console(int)
{
    bd = new CustomSPIFBlockDevice(PTD2, PTD3, PTD1, PTD0);
    fs = new LittleFileSystem("fs", bd);
    int err = fs->mount(bd);
    int err2 = logFile.open(fs,FULL_LOG_FILE_PATH,O_RDWR|O_APPEND);
    return &logFile;
}

Hi Zoltan,

Thank you for the help! The your recommendations seem to get it closer, but I am actually seeing some really strange behavior on the logfile.open call. The value of err2 actually ends up being -22 or -EINVAL due to return statement at File.cpp line 44. It appears that, only when done in the mbed_override_console definition, that _fs is actually set to a value rather than being set to Null, despite never having been assigned. logFile isn’t yet used anywhere else, so how that is being set is quite beyond me. Any ideas of why that would be the case?

I’ll add, if the code is moved out of the mbed_override block, it does not exhibit this behavior.

Maybe the reason is same as for the other stuff: the logFile has not been initialized yet and the _fs data member contains some random value. Try to move the logFile definition into the mbed_override_console function too:

CustomSPIFBlockDevice *bd;
LittleFileSystem *fs;

FileHandle *mbed::mbed_override_console(int)
{
    bd = new CustomSPIFBlockDevice(PTD2, PTD3, PTD1, PTD0);
    fs = new LittleFileSystem("fs", bd);
    int err = fs->mount(bd);
    static File logFile;
    int err2 = logFile.open(fs,FULL_LOG_FILE_PATH,O_RDWR|O_APPEND);
    return &logFile;
}

Frustratingly, event this: produces a -EINVAL error:

CustomSPIFBlockDevice *bd;
LittleFileSystem *fs;
File* logFile;
int err1;
int err2;

FileHandle *mbed::mbed_override_console(int)
{

    bd = new CustomSPIFBlockDevice(PTD2, PTD3, PTD1, PTD0);
    fs = new LittleFileSystem("fs", bd);
    err1 = fs->mount(bd);
    static File log;
    err2 = log.open(fs,FULL_LOG_FILE_PATH,O_RDWR|O_APPEND);
    logFile = &log;
    return &log;
}
  • That’s strange. Try to use the second File constructor:
    /** Create a file on a filesystem
     *
     *  Creates and opens a file on a filesystem
     *
     *  @param fs       Filesystem as target for the file
     *  @param path     The name of the file to open
     *  @param flags    The flags to open the file in, one of O_RDONLY, O_WRONLY, O_RDWR,
     *                  bitwise or'd with one of O_CREAT, O_TRUNC, O_APPEND
     */
    File(FileSystem *fs, const char *path, int flags = O_RDONLY);
  • How/where is FULL_LOG_FILE_PATH defined?

So using the constructor doesn’t return an error code, but a test write does not appear to work afterwards.

This is at the top of the main.cpp
#define FULL_LOG_FILE_PATH "/logs.txt"

  • Try to define the FULL_LOG_FILE_PATH as
    #define FULL_LOG_FILE_PATH "/fs/logs.txt"

  • You can find a very nice explanation how the printf works in Mbed OS 6, including overriding the console, at Hitchhiker's Guide to Printf in Mbed 6
    To redirect the printf it’s sufficient to override the console only for the stdout file descriptor:

FileHandle *mbed::mbed_override_console(int fd)
{
    if (fd == STDOUT_FILENO) {
         ...
        return &logFile;
    }
    else {
        return NULL;  // don't override stdin and stderror
    }
}

So this works:

CustomSPIFBlockDevice *bd = new CustomSPIFBlockDevice(PTD2, PTD3, PTD1, PTD0);
LittleFileSystem *fs = new LittleFileSystem("fs", bd);
int err1 = fs->mount(bd);
File logFileInstance;
int err2 = logFileInstance.open(fs,FULL_LOG_FILE_PATH,O_RDWR|O_APPEND);
File *logFile = &logFileInstance;


FileHandle *mbed::mbed_override_console(int)
{

    // bd = new CustomSPIFBlockDevice(PTD2, PTD3, PTD1, PTD0);
    // fs = new LittleFileSystem("fs", bd);
    // static File logFileInstance;
    // err2 = logFileInstance.open(fs,FULL_LOG_FILE_PATH,O_RDWR|O_APPEND);
    // logFile = &logFileInstance;
    // return &logFileInstance;
    
    static BufferedSerial serial(PTC4, PTC3);  //Works
    return &serial;
}

But this does not:

CustomSPIFBlockDevice *bd;
LittleFileSystem *fs;
File* logFile;
int err1;
int err2;

// CustomSPIFBlockDevice *bd = new CustomSPIFBlockDevice(PTD2, PTD3, PTD1, PTD0);
// LittleFileSystem *fs = new LittleFileSystem("fs", bd);
// int err1 = fs->mount(bd);
// File logFileInstance;
// int err2 = logFileInstance.open(fs,FULL_LOG_FILE_PATH,O_RDWR|O_APPEND);
// File *logFile = &logFileInstance;


FileHandle *mbed::mbed_override_console(int)
{

    bd = new CustomSPIFBlockDevice(PTD2, PTD3, PTD1, PTD0);
    fs = new LittleFileSystem("fs", bd);
    static File logFileInstance;
    err2 = logFileInstance.open(fs,FULL_LOG_FILE_PATH,O_RDWR|O_APPEND);
    logFile = &logFileInstance;
    // return &logFileInstance;
    
    static BufferedSerial serial(PTC4, PTC3);  //Works
    return &serial;
}

Additionally, changing FULL_LOG_FILE_PATH causes a -2 error on the normally working path, so that isn’t the issue.

Where/When does the function actually get executed?