Examples

Annotated examples showing xelp on different platforms and use cases. Full source code is in the examples/ directory.

ExamplePlatformWhat it demonstrates
Bare metalAny MCUMinimal porting template with all three modes
Multi-instanceAny MCUTwo independent CLIs on separate UARTs
Posix simpleLinux / macOSFull interactive demo with ncurses
ArduinoAny Arduino boardLED control, token listing via CLI
Arduino C++Any Arduino boardC++ wrapper class for easy integration
ESP32 WiFiESP32WiFi config, time and weather fetch via CLI
ScriptingLinux / macOSBatch scripting vs interactive mode

Bare metal

File: examples/bare-metal/bare-metal-example.c

The starting point for any new port. Shows the complete xelp integration pattern with no dependencies beyond a UART.

#include "xelp.h"

/* 1. Platform stubs -- replace with your hardware */
static void uart_putc(char c)   { /* UART TX */ }
static void uart_bksp(void)     { uart_putc('\b'); uart_putc(' '); uart_putc('\b'); }
static int  uart_rx_ready(void) { return 0; }
static char uart_getc(void)     { return 0; }

/* 2. KEY mode commands */
XELPKeyFuncMapEntry key_commands[] = {
    { &key_help,   '?', "show help"    },
    { &key_banner, 'b', "print banner" },
    XELP_FUNC_ENTRY_LAST
};

/* 3. CLI mode commands */
XELPCLIFuncMapEntry cli_commands[] = {
    { &cmd_help, "help", "show help"        },
    { &cmd_echo, "echo", "echo args back"   },
    { &cmd_info, "info", "show system info" },
    XELP_FUNC_ENTRY_LAST
};

/* 4. Wire everything up */
XELP cli;

void main(void) {
    XELPInit(&cli, "My Device v1.0\n");

    XELP_SET_FN_OUT(cli,  &uart_putc);
    XELP_SET_FN_BKSP(cli, &uart_bksp);
    XELP_SET_FN_KEY(cli,  key_commands);
    XELP_SET_FN_CLI(cli,  cli_commands);

    XELPHelp(&cli);
    XELPParseKey(&cli, '\n');   /* show initial prompt */

    for (;;) {
        if (uart_rx_ready())
            XELPParseKey(&cli, uart_getc());
    }
}

Key points

Multi-instance

File: examples/multi-instance/multi-instance-example.c

Two completely independent CLIs on separate serial ports. Each has its own output function, command table, prompt, and internal state.

XELP debug_cli, field_cli;

XELPInit(&debug_cli, "Debug Console (UART0)");
XELPInit(&field_cli, "Service Port (UART1)");

XELP_SET_FN_OUT(debug_cli, &uart0_putc);
XELP_SET_FN_OUT(field_cli, &uart1_putc);

XELP_SET_FN_CLI(debug_cli, debug_commands);
XELP_SET_FN_CLI(field_cli, field_commands);

XELP_SET_VAL_CLI_PROMPT(debug_cli, "dbg>");
XELP_SET_VAL_CLI_PROMPT(field_cli, "svc>");

/* Poll both UARTs */
for (;;) {
    if (uart0_rx_ready()) XELPParseKey(&debug_cli, uart0_getc());
    if (uart1_rx_ready()) XELPParseKey(&field_cli, uart1_getc());
}

Use cases

Posix simple

File: examples/posix-simple/xelp-example.c

Full interactive demo for Linux and macOS using ncurses for terminal handling.

make example    # from repo root

Requires ncurses: sudo apt-get install libncurses5-dev (Debian/Ubuntu).

Features demonstrated

Architecture

stdin (ncurses getch)
  |
  v
XELPParseKey(&example, char)
  |
  +--> CLI: line buffer -> tokenize -> dispatch
  +--> KEY: immediate dispatch
  +--> THR: pass-through function
  |
  v
gPutChar() -> ncurses addch() -> terminal

Arduino

File: examples/arduino/arduino.ino

Basic example using the raw C API. Works on any Arduino board with a Serial port — LED control, token listing, and built-in help. No external library dependencies.

void writeChar(char c) { Serial.write(c); }

void setup() {
    Serial.begin(115200);
    XELPInit(&cli, "xelp Arduino example v1.0\n");
    XELP_SET_FN_OUT(cli, &writeChar);
    XELP_SET_FN_CLI(cli, gMyCLICommands);
}

void loop() {
    if (Serial.available() > 0) {
        char c = Serial.read();
        XELPParseKey(&cli, c);
    }
}

Commands

Arduino C++

File: examples/arduino-cpp/arduino-cpp.ino

Same idea as the arduino example but uses the XelpCLI C++ wrapper class from src/XelpArduino.h. Eliminates boilerplate: no manual XELP_SET_FN_* macros, no Serial.available() loop — just begin(), setCommands(), and poll(Serial).

ESP32 WiFi

File: examples/esp32-wifi/esp32-wifi.ino

ESP32 WiFi example using the C++ wrapper. Configure WiFi credentials over the serial CLI, then fetch the current time and weather from free APIs (worldtimeapi.org and open-meteo.com). No API keys needed.

Commands

Scripting

File: examples/scripting/scripting-example.c

Demonstrates the difference between scripting mode (XELPParse / XELPParseXB — execute a buffer of commands at once) and interactive mode (XELPParseKey — character-by-character terminal input).

gcc -Wall -Isrc examples/scripting/scripting-example.c src/xelp.c -o scripting-example
./scripting-example

Writing your own commands

All CLI commands have the same signature:

XELPRESULT my_command(const char *args, int maxlen);

Pattern: parsing arguments

XELPRESULT cmd_set(const char *args, int len) {
    XelpBuf b, tok;
    int n;

    XELP_XBInit(b, args, len);
    XELPNumToks(&b, &n);

    if (n < 3) {
        XELPOut(&cli, "usage: set <key> <value>\n", 0);
        return XELP_E_ERR;
    }

    /* Token 0 = "set", Token 1 = key, Token 2 = value */
    XELP_XBTOP(b);
    XELPTokN(&b, 1, &tok);
    /* tok.s .. tok.p is the key string */

    XELP_XBTOP(b);
    XELPTokN(&b, 2, &tok);
    int value = XELPStr2Int(tok.s, tok.p - tok.s);

    return XELP_S_OK;
}

Pattern: output without printf

xelp has no printf. Use XELPOut for strings. For numbers, use your platform’s sprintf into a local buffer, or write a simple helper:

void print_int(XELP *x, int val) {
    char buf[12];
    int i = 0;
    if (val < 0) { XELPOut(x, "-", 1); val = -val; }
    if (val == 0) { XELPOut(x, "0", 1); return; }
    while (val > 0) { buf[i++] = '0' + (val % 10); val /= 10; }
    while (i > 0) { XELPOut(x, &buf[--i], 1); }
}

Next steps