xelp is an interactive command line interpreter, script engine, and single-key menu system for embedded C projects. It compiles to under 4 KB with all features enabled and runs on targets from 8-bit microcontrollers to 64-bit hosts. Register your own C functions as commands and call them from the prompt or from scripts stored in ROM. No OS, no dynamic memory, no standard library dependencies.
Features
- Pure C (C89 compliant), optional C++
extern "C"support - Compiled sizes: 900 bytes – 4 KB depending on features enabled
- No
malloc, no globals — all state in an instance struct - Bring your own functions — register any C function as a CLI command with a one-line table entry, callable interactively or from scripts
- Multiple independent instances on different serial ports, BLE, USB CDC
- Char-at-a-time parsing — feed from UART, ISR, or any byte stream
- Built-in tokenizer with quoted strings, escape sequences,
#comments - Scriptable — run multi-command const strings from ROM or RAM
- Zero dependencies (no stdio.h, string.h, etc.)
CLI in action
Every command above is a plain C function registered with a one-line table entry:
XELPCLIFuncMapEntry commands[] = {
{ &cmd_echo, "echo", "echo args back" },
{ &cmd_led, "led", "led <0|1>" },
{ &cmd_config, "config", "config <key> <value>" },
XELP_FUNC_ENTRY_LAST
};
Modes
| Mode | Description | Compile flag |
|---|---|---|
| CLI | Command line with prompt, backspace, command dispatch. Type commands, press ENTER. | XELP_ENABLE_CLI |
| KEY | Single key press → immediate action. For menus and quick toggles, no ENTER needed. | XELP_ENABLE_KEY |
| THR | Pass-through to another peripheral (modem, debug port). All keys forwarded. | XELP_ENABLE_THR |
Quick start
Add three files to your project: xelp.c, xelp.h,
xelpcfg.h. Then:
#include "xelp.h"
void uart_putc(char c) { UART_TX = c; }
XELPRESULT cmd_hello(const char *args, int len) {
XELPOut(&cli, "Hello!\n", 0);
return XELP_S_OK;
}
XELPCLIFuncMapEntry commands[] = {
{ &cmd_hello, "hello", "say hello" },
XELP_FUNC_ENTRY_LAST
};
XELP cli;
void main(void) {
XELPInit(&cli, "My Device v1.0");
XELP_SET_FN_OUT(cli, &uart_putc);
XELP_SET_FN_CLI(cli, commands);
for (;;) {
if (uart_rx_ready())
XELPParseKey(&cli, uart_getc());
}
}
Platform support and build sizes
Compiled with -Os, all features enabled:
| Architecture | Compiler | Word size | Code size (bytes) |
|---|---|---|---|
| ARM32 Thumb | arm-none-eabi-gcc | 32-bit | 1488 |
| MSP430 | msp430-gcc | 16-bit | 1804 |
| RISC-V (rv64) | riscv64-linux-gnu-gcc | 64-bit | 1960 |
| ARM32 | arm-none-eabi-gcc | 32-bit | 2372 |
| AVR (ATtiny85) | avr-gcc | 8-bit | 2420 |
| AVR (ATmega328P) | avr-gcc | 8-bit | 2452 |
| x86-64 | GCC | 64-bit | 2775 |
| x86-32 | GCC | 32-bit | 2826 |
| x86-64 | Clang | 64-bit | 3003 |
| ARM64 | aarch64-linux-gnu-gcc | 64-bit | 3148 |
| PowerPC | powerpc-linux-gnu-gcc | 32-bit | 3778 |
| 68HC11 | m68hc11-gcc | 8-bit | 4247 |
Sizes measured via Docker cross-compilation (bash tools/crossbuild.sh).
KEY-only mode: 900 bytes.
Building and testing
make tests # build and run unit tests + coverage
make coverage # tests + coverage summary
make example # build and run the posix ncurses example
make clean # remove build artifacts
19 test units, 207 test cases, 100% line coverage of xelp.c.
Documentation
- Tutorial — step-by-step introduction
- Examples — annotated code for various platforms
- API Reference — all public functions and macros
- Configuration Guide — compile-time options in xelpcfg.h
- Porting Guide — how to bring up xelp on a new platform
- Release History
- Contributing
License
BSD 2-Clause — see LICENSE.txt. © 2005–2025 M. A. Chatterjee