Shell variables in C

I am currently working on a motor control project (post about it coming soon ;)) which requires a lot of parameter tuning of controllers and filters. One way would be to recompile and flash the microcontroller for each new parameter, which takes time and is quite a pain. Hence I’m working with ChibiOS which has a built in shell through serial port or USB CDC I decided to write a simple code that would enable me to change variables during runtime. In the end it was not a lot of work and I am using it, not just for parameters but also for debugging.

1 Data types

The usbcdcParameterStruct_t struct contains the variable name and a pointer to the variable. I am working only with float data types, you could of course use any other type. I put the following code block into the usbcdc.h file of ChibiOS.

typedef struct
{
	const char* name;
	float* loc;
} usbcdcParameterStruct_t;
void usbcdcSetShellVars(const usbcdcParameterStruct_t** vars);

2 Shell code

The module that handles the shell needs the pointer to a list of all variables and a function to set this pointer:

static const usbcdcParameterStruct_t** mShellVars;
void usbcdcSetShellVars(const usbcdcParameterStruct_t** vars)
{
  mShellVars = vars;
}

There are two commands get and set. Each has a callback function that handles the request. The get one is easy. It writes a list of all variables stored at the location of the mShellVars pointer with the corresponding value.

static void cmd_get(BaseSequentialStream *chp, int argc, char *argv[])
{
  (void)argc;
  (void)argv;
  uint8_t ctr;
  for(ctr = 0; mShellVars[ctr] != NULL; ctr++)
  {
    chprintf(chp, "%s = %.3f\r\n", mShellVars[ctr]->name, *mShellVars[ctr]->loc);
  }
}

The set function is also quite straightforward. It searches the list of variable for the matching name and then converts the string to a float to store it.

static void cmd_set(BaseSequentialStream *chp, int argc, char *argv[])
{
  uint8_t ctr;
  if(argc != 2) 
  {
    chprintf(chp, "Too few arguments\r\n");
    return;
  }
  for(ctr = 0; mShellVars[ctr] != NULL; ctr++)
  {
    if(strcmp(mShellVars[ctr]->name, argv[0]) == 0)
    {
      (*mShellVars[ctr]->loc) = stof(argv[1]);
      chprintf(chp, "%s = %.3f\r\n", mShellVars[ctr]->name, *mShellVars[ctr]->loc);
      return;
    }
  }
  chprintf(chp, "%s not found\r\n", argv[0]);
}

3 Usage

To use it you only have to declare which variables you’d like to access through the shell and call the usbcdcSetShellVars functions:

static float mFoo, mBar;
static const usbcdcParameterStruct_t mShellParmFoo = {"foo", &mFoo};
static const usbcdcParameterStruct_t mShellParmBar = {"bar", &mBar};

static const usbcdcParameterStruct_t* mShellVars[] = 
{
  &mShellParmFoo,
  &mShellParmBar,
  NULL
};

int main(void) 
{
  usbcdcSetShellVars(mShellVars);
  while(1);
  return 0;
}

4 Result

ch> get
foo    0.0
bar    0.0
ch> set foo 1
foo = 1
ch> get
foo 1.0
bar 0.0

This method enables you to tweak all the necessary variables during runtime. It requires some flash storage (variable names and code) but it is definitively it. All the code is available here: https://gist.github.com/noah95/149447ccaa8a84e3b515607aad11ea82

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.