Debugging When You’re In A Bind

Published

Debugging When You’re In A Bind

Recently, I worked on a project for a client to send files via FTP from a laptop to an ESP8266 -WROOM-SOM for storage on external SPI flash (there was insufficient space on the internal SPI flash of the SOM to be able to hold all of the files that the client intended). While the file transfer component was straightforward, storage on the external SPI flash was not. The biggest obstacle I faced during this effort was debugging.

Since this SOM is well defined and developed in Arduino, I opted to use it as my development framework. However, Arduino – as part of its “standard” tools – really doesn’t have any appropriate debugging option. While there are sophisticated 3rd party debugging tools, the timeframe for this effort was compressed to the point where I couldn’t afford the time to learn any new tools. Thus, I came up with a poor man’s debugger to help me troubleshoot issues that I faced. But, before we get into this simple technique, let’s go over the specifics of what I was trying to accomplish.

As I mentioned, the goal was to implement a feature that would allow file storage on external SPI flash. Since the file requirements were pretty strict (there would be a set number of files and each would be at most a certain size), I initially thought to divide up the SPI flash into equal segments with each segment being equal to the largest file size. This implementation would keep things simple. However, after speaking with the client and learning that the file size requirement was not as strict as I’d hoped, we determined that the simple solution above would not suffice and that we needed to go with an actual filesystem.

I had originally considered using the SdFat filesystem, which is a library in Arduino (as I mentioned, ESP-based platforms are well-supported in Arduino). Even though I did consider that it was meant for SD cards rather than SPI flash memory, I nonetheless took a look at the library to determine the feasibility of porting it to work with external SPI flash memory. The analysis determined that the porting effort would be substantial and so I looked for another filesystem library.

One such candidate was SPIFFS, which stands for SPI Flash FileSystem. This library was appealing because it was natively used as the filesystem for the internal SPI flash of the ESP8266-WROOM SOM. I didn’t consider it the first time around because I didn’t realize that it could be modified to support external SPI flash as well. While the porting effort was relatively straightforward (all that was needed was to implement a handful of callbacks that the library used to interface with the actual component), I did run into issues during integration testing. Specifically, I was getting all sorts of errors reported by the library when initializing the SPI flash. Some sort of debugging was necessary.

Unfortunately, there were two issues with debugging itself. First, there is no traditional debugger that works seamlessly with Arduino. Yes, there are third party tools that support step-by-step debugging in an Arduino environment, but due to the limited time frame and severely limited budget afforded by the project, there was no opportunity to procure and evaluate any of these tools. Second, the library is implemented in C, and using any of the Arduino Serial printf functions for debugging was not possible since they’re implemented in C++. I devised an alternative that was simple and straightforward. I defined an enum type for each library function I was debugging and a global variable of that enum type. Each value of the enum would correspond to a particular location in the function. The global variable would be set to an initial value at the start of a function and incremented at key points in the function. The global variable would correspond to the location in the function where it ultimately returned. Finally, the calling C++ function would simply print the value of the enum variable to the console (the header file where the enum and global variables were declared needed to be included in the C++ file). The following is an example using some contrived functions:

/**** c_fns.h ****/
typedef enum {
    C_FN_STATE_0 = 0,
    C_FN_STATE_1,
         .
         .
         .
    C_FN_STATE_N
} xCfnState;

xCfnState c_fns_a_state;

/**** c_fns.c ****/
void c_fns_a()
{
    c_fns_a_state = C_FN_STATE_0;
            .
            .
            .
    c_fns_a_state++;
            .
            .
            .
}

/**** cpp_fns.cpp ****/
cpp_fns::cpp_fn_a()
{
    Serial.printf(“Going to call c_fns_a\n”);
    c_fns_a();
    Serail.printf(“State = %d\n”, c_fns_a_state);
}

As seen in the above example, there are a few important considerations to keep in mind. First, depending on how layered the library is, you may end up with many enum types and corresponding variables that will require tracking. Second, printing of the enums must be appropriately sequenced so that an accurate representation of the state of the library is captured.

In summary, using an enum and a corresponding global variable is an effective mechanism to capture the state of a C library when using it in a C++ application. While you must keep a few considerations in mind and must remember that this is not a substitute for a proper debugger, this technique may come in handy when you’re in a bind.

1 comment

Leave a comment

Your email address will not be published.