Command Interpreter Library

11 Mar 2011

Hi,

For those interested I've published a command interpreter some time ago that allows you to define commands, parameter lists and their descriptions.

Import programCmdb_test

Demo of the Cmdb Command Interpreter. Replaces the old cmbd demo as it had name clashes on this website.

Commands can be organised in subsystems (you always see zero or one subsystem together with global commands).

Implementing the commands is done in a switch statement and parameters can be easily accessed after they are parsed.

The cmdb library takes care of parsing commands, generating help, ASCII handling (backspace and previous command) and has a simple option for macros.

Adding a command is a simple two step process,

  • Add a command definition to the cmds table
  • Implement a case statement in the dispatcher routine.

Some (global) commands are handled by the cmdb library internally like 'Help' and 'Idle'. The library also has a number of predefined commands you can add like 'Boot' and Macro Support.

After that it's a matter of feeding the interpreter with serial input.

My main use for this library is to create small & simple pieces of functionality (like setting a PWM port with a command like 'PWM 3 100 10' which sets PWM port 3 to 100ms period with 10% duty cycle) that I can call from a controlling PC.

This keeps my embedded software simple (modular) and not a giant monolithic piece of software. The command interpreter approach makes it also easy to test by hand with good old hyper-terminal or similar programs.

Suggestions or bug reports are welcome!

17 Mar 2011

Wim,

I have tried this and cannot make it work. I imported Cmdb_test and compiled and loaded it to my MBED.

I communicate with the MBED using the Bray terminal program.

The commands, Test, Help Idle and Commands all work as expected but "Int O" gives the following

"CMD>int 0 Unknown command, type 'Help' for a list of available commands. CMD>"

I thought it was working at one time but when I started to add my own commands it failed so I reloaded the complete code and at that stage discovered the problem.

Any help or advice will be welcome, particularly as this looks a very useful starting for my current project. Thanks for making available all your work on it.

Allan

17 Mar 2011

Hi

The 'Int' demo command is located in the test subsystem.

So you have to enter 'Test' (without quotes and followed by enter) first to switch. If you type help you'll see the extra commands from the sunsystem merged into the help output.

To return to the top level there is the 'Idle' command.

Wim

17 Mar 2011

Hi,

The idea was that you only have a limited subset of commands available to minimize the help, prevent (user) errors, keep it simple and functionally organized.

By typing the name of another subsystem you can also switch directly between them. You can see the active subsystem in the prompt (for test it should Show 'TEST>' instead of 'CMD>').

In my apps I have for instance a subset ADC that contains all ADC releated commands (like setting sample times and perform single/multiple conversions). In a PID subsystem I can issue PID related commands. You can also mark a subsystem as hidden so it will not show up in the help.

Wim

17 Mar 2011

Wim,

Many thanks for this help, particularly as it seems I did not read the documentation fully, sorry for wasting some of your time.

With your information I have made some progress in that if I send "Int 0" in CMD state I get an error message.

If I use "Test" to put me in a the TEST state then all of the standard commands I have tried work, crucially "Idle" takes me back from TEST to CMD.

Unfortunately if I send "Int 0" when in TEST state the MBED locks up after merely echoing the input string. The messagae inside the callback function my_dispatcher is not shown.

Where do I go next please ?

Allan

17 Mar 2011

Hi,

No mbed at hand at the moment. I'll try to test it this evening (and post a reply)!

Btw does it print the:

cmdb.printf("my_dispatcher: cid=%d\r\n", cid);

message?

Wim

17 Mar 2011

Wim,

No it does not.

Thank you for your help

Allan

17 Mar 2011

Hi Allen

Did you change anything to the serial port setup (or other code)?

It seems like it crashes on the call to the dispatcher for user commands (I'll look into it).

I use the code myself so it should work. The subsystems are handled by the library so work witthout the dispatcher.

Wim

17 Mar 2011

Hi Allen,

Just compiled my code again and tested it to be working fine.

Wim

24 Mar 2011

Wim,

I still having problems.

Everyhting works apart from Int being recognised when in test mode, below is a snippet of my communications bwteen my terminal program and my MBED.

CMD>Int 1 Unknown command, type 'Help' for a list of available commands. CMD>test Test>Int 1

I needed to reset the MBED after the second "Int 1"

One thing I do notice is that the information about Int given by command shows it to be in the subsytem Int, the snippet from command is given below

[command02] type=Command subsystem=Int command=Int helpmsg=* Int as parameter parameters=int syntax=dummy

This slightly perplexes me as I would gave expected it to be Test.

Regards

Allan

24 Mar 2011

Hi Allen

The output of commands is here too

[command02]
type=Command
subsystem=Int
command=Int
helpmsg=* Int as parameter
parameters=int
syntax=dummy

I have found the cause of the corruption. If you change the two defines like:

  1. define CID_TEST (int)0
  2. define CID_INT (int)1

it should work for now (I'll fix the problems as soon as i can, i already fixed one but am scanning for more of the same). The problem is a mix-up between vector indexes and id's. As my old code was enum based it was never a problem.

24 Mar 2011

Hi

I just updated the library.

Still it's strange that your mbed crashes on the demo program where mine runs happily with the bugged code. Anyway it should be fixed now.

Please let me know if everything works like it should!

Wim

25 Mar 2011

Wim,

Using the latest library and the edits to the #define lines I now have it working correctly, at least that is my interpretation of the log file below

Boot

Cmdb Command Interpreter Demo Version 0.77.

CMD>Test Test>Int 8 my_dispatcher: cid=1 my_dispatcher: parm 0=8 Test>Idle CMD>Int 8 Unknown command, type 'Help' for a list of available commands. CMD>

That is we rcognise "Int 8" when in "Test" mode but gracefully reject it when in base mode.

Now all I have to do is to add my own functions and I am away.

Many thanks for you help

Allan

25 Mar 2011

Hi,

Could you please try it with 1 based defines (like the original code). It should work fine now.

Glad to be of help (and thanks for showing me where things went wrong).

I use this kind of code to let C++ generate the id's (an enum). OIt was also the cause of me not seeing the bug.

#define MY_CMD_TBL_LEN  CID_MY_LAST

enum {
    //'Test' subsystem
    CID_TEST,
    CID_INT,

    CID_MY_LAST
};

const struct cmd mycmds[MY_CMD_TBL_LEN] = {
    cmd( "Test",           SUBSYSTEM   ,CID_TEST        ,""                 ,"* Test Subsystem"             ,""),
    cmd( "Int",            CID_TEST    ,CID_INT         ,"%i"               ,"* Int as parameter"           ,"dummy")
}

void init_interpreter(std::vector<cmd>& cmds) {
    for (int i=0;i<MY_CMD_TBL_LEN;i++) {
        cmds.push_back(mycmds[i]); //Handled by Cmdb internally.
    }
}

By calling the init_interpreter() method, i just add all commands in one batch to the cmds vector. CID_LAST is just a dummy commandId not implemented for getting the size of the enum.

Btw the code is a small portion of my main app I'm developing so some comma's may be missing.

Wim

27 Mar 2011

Wim,

It works correctly with the 1 based defines now.

Thanks

Allan