Micro Shell

2025-11-07

Project repository

Overview

A simple, lightweight shell library with a completely static memory footprint. I developed this as part of NyxINS — a project exploring sensor fusion with consumer grade IMU components — which I ended up shelving after landing a job where I deal with similar topics on a daily basis. The shell itself is modular and useful enough to stand on its own, and I will likely reuse it in other projects.

It's completely platform agnostic, OS/non-OS, doesn't matter. It only requires you to provide implementation for two abstractions:

void shell_putc(const char c)
{
    // Put single character to the interface
}
int shell_getc()
{
    // Get single character from the interface
}


This allows you to define the shell on any interface you need, be it USB, UART or stdout/stdin on Linux. You define the commands like this:

shell_state_t state = device_shell_get_default_state(); // Get default config
shell_command_t commands[] = { // Define command array providing function pointers
    {"foo", foo, "Foo command"}, // Command name, function pointer, help string
    {"bar", bar, "Bar command"},
    {"dead", dead, "Dead command"},
    {"beef", beer, "Beed command"},
};
uint8_t num_commands = sizeof(commands) / sizeof(shell_command_t);
state.shell_commands = commands; // Assign the array and it's size
state.num_commands = num_commands;


And that's it, you just need to include the following in your main program loop:

device_shell_parse(&shell_state)


If initialized properly, you get an automatic help command. The shell also has a weakly defined function that gets called on the first clear of the buffers (at initialization). Because it's weakly defined you can override it with a custom implementation to print a banner on init, run whatever you need when the shell is created:

void shell_greeting_hook()
{
    const char* banner[] = {
        "╔════════════╗\n",
        "║   BANNER   ║\n",
        "╚════════════╝\n"
    };

    for(int i=0; i<sizeof(banner) / sizeof(banner[0]); i++)
    {
        shell_puts(banner[i]);
    }
}


Here is the shell running on my Linux machine from a test binary, the interface is just set to stdout/stdin.

Fig.1 - Example binary with the shell library.


Worth noting is this shell is not thread safe and using it from multiple threads will lead to race conditions.