Shell Customization

Upon completing STM Shell integration, you get a prompt. However, it doesn’t do anything no matter what command you enter. That’s because you still need to create commands your shell can execute. Those commands will be specific to your application so of course you can’t expect me to code them for you.

Not for free, at least.

I will, however, give you a “demo” that you should import into your project right away. It’ll serve as the example for the documentation I’m about to foist upon you. It’s OK, you can easily delete it once you’re done or use it as a template.

This page introduces shell customization, which is essentially UI design for your microcontroller. So it’s less about programming and more about “UX” (user experience). Rest assured, there’s still plenty of programming involved.

Shell customization boils down to two things :

  • Writing functions you want to launch from the command line
  • Declaring those functions as commands in the shell’s pseudo-filesystem

1.   The Demo

Shell customization can be implemented as a single source file. I’ve opted to distribute such a file, for demonstration purposes, through GitHub. I’ve named it “shell_pfs.c” and it’s got its own repository :

https://github.com/Nefastor-Online/STM_Shell_Demo

However, you will not be cloning it. This is just an example and template for shell customization. The whole point is to make it specific to your application, therefore you should just download it and make it your own. In terms of version control, it should logically be tracked with your project’s application code, not with STM Shell.

One reason I’ve given it its own repo is to distribute it alongside STM Shell itself. As far as I know you can’t use GitHub as file host, only a repo host.

I might create slightly different versions of this file to target different STM32 boards in the future. So you should :

  • Check the available branches to see if anything pikes your interest
  • Select that branch, or stick to the master branch
  • Download the “shell_pfs.c” file into your project’s “Core/Src” directory

You also need to add the SHELL_ROOT_BLOCK symbol to your project’s compilation options. To do that, right-click your project in the Project Explorer pane and select “Properties”. Under “C/C++ Build”, “Tool Settings”, “MCU GCC Compiler”, “Preprocessor”, add SHELL_ROOT_BLOCK to all the configurations :

If you’re unfamiliar, this setting is equivalent to adding “#define” macros that apply everywhere in your project. The effect of SHELL_ROOT_BLOCK is to disable the compilation of placeholder code in “shell_command.c” that will be replaced by your custom code in “shell_pfs.c”. More on that later.

While you’re here, there’s a second symbol you may need to add. The demo commands in “shell_pfs.c” use one hardware-specific part : an LED driven by a GPIO. If your board has one, make sure the GPIO pin is balled “LED”. Otherwise, add the SHELL_NO_LED to the symbols. This will replace the LED control macro in “shell_pfs.c” with a dummy.

Click “Apply and Close”. You may be told that an index needs to be rebuilt, that’s perfectly normal.

Build your project. There should be no error or warning. If that’s not the case, review your work and try again.

And now, let’s get boring.

2.   Pseudo Filesystem

Let’s start with a detailed description of how STM Shell implements a custom user interface.

As I’ve mentioned at the start of all this, ages ago, a key aspect of my design is to keep the users’ learning curve as flat and short as possible. To that end, I’ve taken inspiration from typical UNIX-style shells :

  • Commands are organized into pseudo-directories that can be listed with the “ls” command
  • Directories can be nested and navigated using the “cd” command
  • The commands themselves are like pseudo-files in those pseudo-directories

There are many advantages to this approach that I’m not going to sell you on : as you start customizing and using your first shell, they will all become self-evident.

As you’ve certainly guessed, customizing STM Shell means customizing its PFS (Pseudo-FileSystem). That is why the file you just added to your project is named “shell_pfs.c”.

The PFS is programmed as a set of arrays that contain PFS entries. A PFS entry is a simple data structure that contains a command’s name (what the user needs to type), a command description (what the shell displays when you list commands) and a pointer to the function that implements the command. If an entry points to another array of entries, then that array becomes a subdirectory.

Those arrays are called PFS blocks.

3.   PFS Blocks

There are three different blocks in the PFS when you start a new project : the shell, system and root blocks. You can find them in “STM_Shell/Src/shell_commands.c”

The shell block contains the commands native to the shell itself (“ls” and “cd..”). It is part of the library and you have no reason to modify it, as it contains commands that are not specific to your application.

The system block contains application-specific commands that can be used from anywhere in the PFS (system-wide, hence the name). Placing a command in this block is equivalent to adding it to your “PATH” environment variable on your computer. For obvious reasons, this block does not support nesting.

The root block contains the application-specific commands that belong in the root of the PFS. After a reset of your microcontroller, STM Shell starts at the root block.

I wanted to allow compilation so you could check your shell integration prior to customizing it, thus STM Shell declares the system and root blocks, but they are both empty. That is why you added the SHELL_ROOT_BLOCK macro in your project settings. If you do not use system-wide commands, you can ignore the system block, otherwise you can override mine using a similar mechanism (and the macro SHELL_SYSTEM_BLOCK). But you need to create your own root block with your own commands. That is not optional.

You’ll learn to do this in two steps : first, how to write command functions, then how to organize them into a PFS.

But before we get to that, let’s use the demo commands so you can get a feel for how the shell works. This isn’t just recreational, it will inform the way you code your commands and organize them.