Tutorial
A step-by-step introduction to xelp. By the end you will have a working CLI with custom commands, KEY mode shortcuts, scripting, and token parsing.
1. Hello World — minimal CLI
The smallest useful xelp program: one command, one output function.
#include "xelp.h"
/* Platform: write one char. Replace with your UART TX. */
void my_putc(char c) { putchar(c); }
void my_bksp(void) { my_putc('\b'); my_putc(' '); my_putc('\b'); }
/* Your first command */
XELPRESULT cmd_hello(XELP *ths, const char *args, int len) {
XelpOut(ths, "Hello, world!\n", 0);
return XELP_S_OK;
}
/* Command table */
XELPCLIFuncMapEntry commands[] = {
{ &cmd_hello, "hello", "say hello" },
XELP_FUNC_ENTRY_LAST
};
XELP cli;
int main(void) {
XelpInit(&cli, "Tutorial v1.0");
XELP_SET_FN_OUT(cli, &my_putc);
XELP_SET_FN_BKSP(cli, &my_bksp);
XELP_SET_FN_CLI(cli, commands);
int c;
while ((c = getchar()) != EOF)
XelpParseKey(&cli, (char)c);
return 0;
}
Compile: gcc -Isrc src/xelp.c tutorial.c -o tutorial
What just happened
XelpInitzeroes all internal state and stores the about message.XELP_SET_FN_OUTtells xelp how to emit characters on this platform.XELP_SET_FN_BKSPtells xelp how to handle destructive backspace.XELP_SET_FN_CLIregisters your command table.XelpParseKeyfeeds one character at a time. When ENTER is received, xelp tokenizes the line and dispatches to the matching command.
2. Commands with arguments
Command functions receive the raw argument string and its length. Use the tokenizer to extract individual arguments:
XELPRESULT cmd_add(XELP *ths, const char *args, int len) {
XelpBuf b, tok;
int a, result;
XELP_XB_INIT(b, args, len);
/* Token 0 = command name ("add"), 1 = first arg, 2 = second arg */
XELP_XB_TOP(b);
XelpTokN(&b, 1, &tok);
a = XelpStr2Int(tok.s, tok.p - tok.s);
XELP_XB_TOP(b);
XelpTokN(&b, 2, &tok);
result = a + XelpStr2Int(tok.s, tok.p - tok.s);
/* result now holds the sum */
return XELP_S_OK;
}
Register it: { &cmd_add, "add", "add <a> <b>" }
Tokenizer rules
| Syntax | Meaning |
|---|---|
| spaces / tabs | Token separators |
"hello world" | Quoted string = single token |
# | Comment (rest of line ignored) |
; | Statement separator: hello; add 1 2 |
` (backtick) | Escape next character at CLI |
\ (backslash) | Escape inside quoted strings |
3. Counting and iterating tokens
XELPRESULT cmd_args(XELP *ths, const char *args, int len) {
XelpBuf b, tok;
int n, i;
XELP_XB_INIT(b, args, len);
XelpNumToks(&b, &n);
for (i = 0; i < n; i++) {
XELP_XB_TOP(b);
XelpTokN(&b, i, &tok);
XelpOut(ths, tok.s, tok.p - tok.s);
XelpOut(ths, "\n", 0);
}
return XELP_S_OK;
}
4. KEY mode — single keypress actions
KEY mode fires a function on every keypress, no ENTER needed. Great for menus and quick toggles:
XELPRESULT key_toggle_led(XELP *ths, XELPKEYCODE c) {
(void)c;
/* toggle your LED here */
XelpOut(ths, "LED toggled\n", 0);
return XELP_S_OK;
}
XELPKeyFuncMapEntry key_commands[] = {
{ &key_help, '?', "show help" },
{ &key_toggle_led, 'l', "toggle LED" },
XELP_FUNC_ENTRY_LAST
};
XELP_SET_FN_KEY(cli, key_commands);
Mode switching
| Key | Mode | Behavior |
|---|---|---|
| ESC | KEY | Each keypress = immediate action |
| CTRL-P | CLI | Line-buffered with prompt, ENTER to execute |
| CTRL-T | THR | All keys forwarded to pass-through function |
These keys are configurable in xelpcfg.h.
5. Scripting
Any command sequence can be run as a script. Scripts are const strings — they can live in ROM:
const char *startup = "hello; add 10 20; echo done";
XelpParse(&cli, startup, XelpStrLen(startup));
Multi-line scripts:
const char *script =
"# configuration script\n"
"set mode 1\n"
"set gain 50\n"
"echo config complete\n";
XelpParse(&cli, script, XelpStrLen(script));
6. Help system
With XELP_ENABLE_HELP defined, XelpHelp(&cli)
prints all registered KEY and CLI commands. The third field in every command
table entry is the help text:
{ &cmd_hello, "hello", "say hello" },
/* ^^^^^^^^^^^ shown in help output */
7. Multiple instances
xelp uses no globals. Run independent CLIs on different ports:
XELP debug_cli, field_cli;
XelpInit(&debug_cli, "Debug Console");
XelpInit(&field_cli, "Field Service");
XELP_SET_FN_OUT(debug_cli, &uart0_putc);
XELP_SET_FN_OUT(field_cli, &uart1_putc);
XELP_SET_VAL_CLI_PROMPT(debug_cli, "dbg>");
XELP_SET_VAL_CLI_PROMPT(field_cli, "svc>");
8. Numeric parsing
Decimal, hex with 0x prefix, and hex with h suffix:
int val;
val = XelpStr2Int("255", 3); /* 255 decimal */
val = XelpStr2Int("FFh", 3); /* 255 hex suffix */
val = XelpStr2Int("0xFF", 4); /* 255 hex prefix */
val = XelpStr2Int("0x1A", 4); /* 26 uppercase hex */
/* Safer variant with error checking */
XELPRESULT r = XelpParseNum("0xFF", 4, &val);
if (XELP_T_OK(r)) { /* val == 255 */ }
9. THR (pass-through) mode
Redirect all keystrokes to another peripheral — useful for talking to a modem or second MCU through the same terminal:
void modem_send(char c) { /* forward to modem UART */ }
XELP_SET_FN_THR(cli, &modem_send);
CTRL-T enters THR mode, CTRL-P returns to CLI mode.
10. Mode change callback
void on_mode_change(int mode) {
if (mode == XELP_MODE_CLI) XelpOut(&cli, "[CLI]\n", 0);
else if (mode == XELP_MODE_KEY) XelpOut(&cli, "[KEY]\n", 0);
else if (mode == XELP_MODE_THR) XelpOut(&cli, "[THR]\n", 0);
}
XELP_SET_FN_EMCHG(cli, &on_mode_change);
Next steps
- Examples — annotated code for various platforms
- API Reference — complete function docs
- Configuration Guide — compile-time options
- Porting Guide — platform-specific notes