NyxINS

2025-11-07

Project repository

Overview

NyxINS is a project I started to get some hands on experience with sensor fusion and to potentially explore some more advanced PCB design challenges. What I want to achieve with Nyx is I want to see how accurate can I make an INS system that is built with consumer grade components. My general idea here is to employ multiple identical IMU sensors in different orientations, encapsulated in a temperature controlled environment.

I'll be adding more here as I develop the project.

Micro shell

Anticipating future need for access to the device while it's running, modifying values without constant reflashing I developed a simple, lightweight shell library with a completely static memory footprint. It's completely platform agnostic, OS/non-OS, doesn't matter, I was testing it on my Linux machine. 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 a Linux (altough this is probably useless) or others. 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.


I think this itself is modular and useful enough to be migrated to a separate repository. I will likely reuse this component in my other projects.