libshell

Shell building C-library for STM32 and MSP430G2553 MCUs 

1. Introduction

“libshell” is part of “libemb“. It provides the environment needed to build interactive (remote-) shells. It helps with defining commands and callback handlers as well as with the command and argument parsing. The library is not HW dependend and it has no dependencies to other “libemb” libraries.

For detailed building instructions see instructions for “libemb

2. Hardware Setup

No special hardware setup is required. You may consider using “libshell” together with “libconio”, in which case you may want to connect a USB-to-serial converter to your MCU (see “libconio” and “libserial” HOWTOs for more details).

3. Including libshell Headers

To use “libshell” functionality add the following include to your sources:

#include <libemb/shell/shell.h>

4. Linking libshell Library

To link against “libshell”, add the following to your linker options:

-lshell

5. Initializing the Hardware

For “libshell”, no hardware initialization is needed.

6. Define Shell Commands

The core of a shell are the commands it provides. Thus, “libshell” offers a simple way to define commands with description and a callback handler for command processing.

1. Shell Command Struct

The struct used to define commands is called “shell_cmds”. Each entry for a new command must be provided with the following attributes:

Attribute Description
Command name “cmd” used to identify the command
Command description “desc” used to describe your command e.g. when user requests help
Command callback handler “func” function to execute for that command

The example below shows the definition of the two shell commands “h” and “argt”:

shell_cmds my_shell_cmds = {
     .count 	= 2,
     .cmds	= {
          {
               .cmd     = "h",
               .desc	= "list available commands",
               .func 	= shell_cmd_help,
          },
          {
               .cmd     = "argt",
               .desc	= "print back given arguments",
               .func 	= shell_cmd_argt,
          },
     },
};

Note: to later on know the total number of commands defined, “count” has to be set to the correct number.

As you can see in the example, the struct does not store information about parameters taken by an argument. Each callback handler is provided with the arguments given to a command, and the handler function itself has then later on to decide what to do if to many, to less or wrong arguments are given.

7. Write a Callback Handler

A callback handler for a shell command must have the following format:

int (*func)(shell_cmd_args *args);

The “shell_cmd_args” parameter represents the arguments given for the command.

The callback handler for our example command “h” could look something like this (we assume it used none of the given arguments):

int shell_cmd_help(shell_cmd_args *args)
{
    // OPTIONAL: perform check on given arguments ...
    // OPTIONAL: convert arguments (e.g. to integer)

    int i;

    for(i = 0; i < rshell_cmds.count; i++) {
        cio_printf("%s, %s\n\r", rshell_cmds.cmds[i].cmd), rshell_cmds.cmds[i].desc);
    }

    cio_print((char *)"OK\n\r");

    return 0;
}

And the callback handler for our example command “argt” could look something like this (we assume all given arguments are relevant):

int shell_cmd_argt(shell_cmd_args *args)
{
    // OPTIONAL: perform check on given arguments ...

	int i;

    cio_print((char *)"OK\n\rargs given:\n\r");

    for(i = 0; i < args->count; i++) {
         cio_printf(" - %s\n\r", args->args[i].val);
    }

    return 0;
}

8. Helper Functions for Argument Processing

To make processing of arguments a little more convinient, the following mathods are provided by “libshell”:

Method Description
shell_str_len Return the length of a null terminated string
shell_str_cmp Comapre two strings up to a given length for each
shell_parse_int Parse the integer value from a given string

1. String Length

To check the length of a string call “shell_str_len” like so:

char *str = "abc";

int l = shell_str_len(str); 	// l = 3

2. Compare Strings

To compare two strings use “shell_str_cmp”:

char *s1 = "abc";
char *s2 = "abc";
char *s3 = "ABC";
char *s4 = "ABCDE";

int l1 = shell_str_len(s1);
int l2 = shell_str_len(s2);
int l3 = shell_str_len(s3);
int l4 = shell_str_len(s4);

int r;

r = shell_str_cmp(s1, s2, l1, l2); 	// r = 0
r = shell_str_cmp(s1, s3, l1, l3); 	// r = 2
r = shell_str_cmp(s3, s4, l3, l4); 	// r = 1

To parse the integer value of a string use “shell_parse_int”:

int i = shell_parse_int((char *)"42");	// i = 42

9. Process Shell Commands

Since “libshell” does not know when or from what input to process your commands, the using code has to implement and call “shell_process” on its own. To make this an easy task, the “shell_process_cmds” helper function is provided.

For our example commandes defined in “my_shell_cms” previously, “shell_process_cmds” would be implemented as follows:

int shell_process(char *cmd_line)
{
     return shell_process_cmds(&my_shell_cmds, cmd_line);
}

Then, when a new command line arrived from the input of your choice, just call “shell_process” with that command line as input, and “libshell” does the rest for you (check if the command is defined, call the callback function, provide arguments form the commandline to the callback):

char *cmd_line = "argt 1 2 3 4"; // example cmd line

int s = shell_process(cmd_line);

The result from “shell_process” should be checked against the following return values:

switch(s)
{
   case SHELL_PROCESS_ERR_CMD_UNKN:
      // Unknown command
      break;
   case SHELL_PROCESS_ERR_ARGS_LEN:
      // Argument to long
      break;
   case SHELL_PROCESS_ERR_ARGS_MAX:
      // Too many arguments
      break;
   default:
      // OK
      break;
}

Leave a Reply

You must be logged in to post a comment.