In your project, there is an include file named 'my_commands.h'. The commands which will be included in the build are all compiled as a result of this file. Command functions and the macros which add the command to the global linked list of commands are all in this file or the files which are included from this file.
You can look at 'kcmd_basic_commands.h' for a simple example.
Here is how the '+' command is implemented.
void kcmd_Plus(KCMD
*pKCMD)
{
int i1, i2;
i1=kcmd_stackPop(pKCMD);
i2=kcmd_stackPop(pKCMD);
kcmd_stackPush(pKCMD, i1+i2 );
}
The first 7 lines are just a C function. What matters is that the function must have one argument which is a pointer to a KCMD structure, and must return void. As long as it does those two things, the compiler will be happy. This particular function pulls two numbers off the stack, adds them together, and pushes it back on the stack. That's all we need to know about the C function.
The next three lines are what add the command to the linked list of commands. The only one with any work to do is the first line. Here you must specify some names, reference the C function, and write some help text.
MAKE_KCMD_COMMAND_5( plus, "+", kcmd_Plus,
"[a][b] -- [a+b] Adds top two stk elems", g_str_system );
#undef PREVNAME
#define PREVNAME
G_ME(plus)
The first line, with the MAKE_KCMD_COMMAND_5 macro, is where everything gets specified.
'plus' is the normal-text-only name of the command no funny symbols or spaces in here, please. Underscores are ok, but no other punctuation type characters are allowed.
The "+" argument is the name of the command that the user will see. Obviously most any printable character is ok here. Do not put any spaces in the name of the command, because the command processor will split the word in two when the user tries to use the name on the command line.
The next argument, 'kcmd_Plus', is just the exact name of the C function.
After that is the help text. This can contain punctuation and spaces.
The final argument is a pointer to constant character string Category name. g_str_system makes it a 'system' command, which is the default.
After the MAKE_KCMD_COMMAND_5 macro, is the line which undefines the symbol PREVNAME. This is always there.
After the undef, we redefine the PREVNAME symbol to be G_ME(plus), which MUST BE THE SAME NAME AS THE FIRST ARGUMENT
int the MAKE_KCMD_COMMAND_5. If it isn't, you can get compile errors, or else a messed up list of commands.
In your custom command you will frequently want to type text to the user. If you use the right functions, your commands will work on all platforms.
For simply sending some constant text to user, use the function print_kccstr(). This will simply print the const char string to the user using the IO functions they provided when the command processor was initialized. It is important for some platforms that the proper macros are used. In the example below, see how the string to be printed is enclosed by the KCCSTR() macro. This is important for the AVR compiler, because all constant strings should go into Flash memory.
And it is more serious than simply wasting memory. On the AVR platform, it is expected that any function which takes a KCCHAR * argument MUST have its contents stored in flash. If one of these functions gets a pointer to a character array in RAM, the routine will simply start reading gibberish. So although the special macros can be ignored on the Windows platform, this will make your command unportable to other platforms.
void kcmd_Easter(KCMD
*pKCMD)
{
print_kccstr(pKCMD,
KCCSTR("\r\negg.\r\n"));
}
MAKE_KCMD_COMMAND( easter, kcmd_Easter, "[] --
[] Celebrates Easter", g_str_system );
#undef PREVNAME
#define PREVNAME
G_ME(easte
r)
Frequently it is desired to print out several numeric arguments in a formatted way. There 6 functions provided which provide printf-like output for 0 through 5 arguments. This is a stripped-down printf, which only handles strings and integers. The format specifiers permitted are:
%s string. The argument would be a KCHAR *, not a KCCHAR *. So this string would be expected to be in RAM.
%d decimal. Just like printf(). Supports the width field and a leading zero. So %02d would print two digits with a leading zero.
%x hex. Just like printf(). The leading zero and width fields are supported. SO 0x%04x prints a 4 digit hex number with leading zeros.
%p. Just like %x, except it prints the '0x' prefix for you.
NOTE: The format specifier string MUST be a KCCHAR *. Notice how the KCCSTR() macro is used in the case below. If it is omitted, the command will not work on the AVR platform, and possibly many other platforms.
Note also that the function kcmd_Print_2() was used. That is because there were 2 arguments after the format string. If we wanted to add another argument, we'd have to change to the function kcmd_Print_3().
void kcmd_memstat(KCMD *pKCMD)
{
extern int g_nBytesAllocated;
extern int g_nMaxBytesAllocated;
kcmd_Printf_2(pKCMD, KCCSTR("\r\n Cur Bytes
Allocated: %d Max: %d\r\n"),
g_nBytesAllocated,
g_nMaxBytesAllocated);
}
Most commands that need simple numeric parameters can get the from the stack. The user will put the numbers on the stack before typing the command name. The command function can retrieve the values from the stack with simple functions:
void kcmd_Plus(KCMD *pKCMD)
{
int i1, i2;
i1=kcmd_stackPop(pKCMD);
i2=kcmd_stackPop(pKCMD);
kcmd_stackPush(pKCMD, i1+i2 );
}
It is possible to make a command which calls the user's IO function directly to get character input. For example it would be possible to go into a mode where you control a robot directly by looking for arrow keys or letter keys.
The KCMD structure holds a pointer to the USER_IO structure. This is a structure of function pointers to IO functions. So for example, to get a single character from the user, do the following:
ch =
pKCMD->UserIO->pFuncGetc(pKCMD->conio_context, &iError,
100);
This will call the user's GetC function to get a character. iError will never be used, and the timeout value of 100 will be ignored.
But in other cases, we might want to go into a mode where we continously spew output, and we want the spewing to be interrupted by the user typing a key. In that case we call the user's kbhit function directly:
if(
pKCMD->pUserIO->pFuncKbhit(pKCMD->conio_context))
{
break;
}
It is conceivable that it might be required to read a character string from the command line as a parameter to a command. Look at the code for the 'help' command to see how it is done. Instead of putting the parameter before the command, in this case, the command will look at the rest of the command line after itself. The remaining text on the command line is stored in the KCMD structure, so it is easy to parse the next token and remove it from the KCMD structure.
The suggested place to add custom commands is by creating a new include file. In that include file, add all the custom commands that are required. Then in the my_commands.h, have the new include file get included from the proper location. Let's take a close look at what is in my_commands.
The first file which gets included in my_commands.h is kcmdCommandMacros.h.
/**
* Include the file with the macros for defining commands in
it.
*/
#include "kcmdCommandMacros.h"
This include file contains the macro definitions for adding commands.
Further on down the file, a preprocessor symbol is define:
#define PREVNAME g_kcmd_bye
This symbol represents the last command to be added to the linked list. We want the 'bye' command to be the last on the list. Hence this command is actually defined in the kcmd.c core. With each command added, PREVNAME gets redefined in order to make the linked list work. If this doesn't make sense, don't worry about it, you don't need to know how it works in order to make new commands.
Next comes an included file which defines all the basic commands:
/**
* kcmd_basic_commands is a file with the basic commands in
it.
*/
#include "commands/kcmd_basic_commands.h"
This include file will have the same form as the custom include file we are going to make for our custom commands. So we are going to open it up soon.
Next in 'my_commands.h' comes the include file we are going to create:
/****************************************************************************
* Here is where more include files with more commands would be
added.
* Simply follow the model of kcmd_basic_commands.
****************************************************************************/
//#include "my_custom_commands.h"
We will create a my_custom_commands.h file and put some commands in it. But first, look what comes after:
/****************************************************************************
* Here we define the last command in the list. It is important that
this
* stay at the END of this file, because it is only command which has
a
* pNext == NULL.
****************************************************************************/
#include "commands/kcmd_first_command.h"
This include file will clean up after the custom commands include, and so this file MUST be included last, after all the other includes.
Create an include file called 'my_custom_commands.h'. Let's add a custom command to it. Here is a basic command which pops the current temperature in degrees farenheit from the top of stack, then types out a message based on that temperature.
void kcmd_temp(KCMD *pKCMD)
{
if(pKCMD->iStack==0)
{
print_kccstr(pKCMD,
g_str_stackEmpty);
} else
{
int i;
i=kcmd_stackPop(pKCMD);
if(i<70)
{
kcmd_Printf_1(pKCMD, KCCSTR("\r\n%d degrees is
chilly"), i);
} else if(i>76)
{
kcmd_Printf_1(pKCMD, KCCSTR("\r\n%d degrees is
too hot"), i);
} else
{
kcmd_Printf_1(pKCMD, KCCSTR("\r\n%d degrees is
just right!"), i);
}
}
}
MAKE_KCMD_COMMAND_5( temp, "temperature",
kcmd_temp, "[temp] -- [] analyzes temperature", g_str_system );
#undef PREVNAME
#define PREVNAME
G_ME(temp)
Here is the result of running it.
PROMPT> 65 temperature
65 degrees is chilly
PROMPT> 80 temperature
80 degrees is too hot
PROMPT> 72 temperature
72 degrees is just right!
PROMPT>
You can add categories of commands just by creating a global character array and then using it in the last argument of the MAKE_KCMD_COMMAND_5 macro. Then users can get help listings of just the commands in a particular category.
For example, say you define three new commands, spewADC, printADCStatus, and shutdownADC. You want these three commands to be a category called ADC. So you first define a global array of characters with the string 'ADC' in it.
KCCHAR KCCATTR g_str_adc[] = KT("ADC");
Don't be worred about the fancy types and macros. This is done to be platform independent, but it basically boils down to:
const char g_str_adc[] = "ADC";
However, if you want your command to work all platforms, use the 'KCCHAR' version.
And for all three of these commands you specify a string g_str_adc as the last argument in the MAKE_KCMD_COMMAND_5 macro.
And... you're done. Since these commands are not in the default category, now when you type 'help' you will need to specify either 'help system' or 'help adc'. The command lists are now seperate. Note that the only reason for making categories is to keep the command list from getting too long. If the KCMD is used to make hundreds of commands, it will be much better for the user if she can list just the ADC commands instead of getting a huge overwhelming list that is too long to be useful.