A lightweight CLI library for constrained embedded systems
- Non-blocking: Suitable for single-threaded apps without blocking the main loop
- No dynamic memory: Uses static buffers only
- Hardware-agnostic: Abstract IO interface supports various communication methods
Packaged in are IO adapters for...
- Arduino Serial Library
- ESP32 UART
- ESP32 STA Wifi
- Microblaze V AXI Uartlite (soon)
Several implemented examples can be found in the mcli-examples repo.
The GIF below shows the ESP32 WiFi CLI demo in action, featuring:
- Telnet compatibility via PuTTy
- Built-in
helpcommand - Custom command definitions
- Connection recovery after disconnect
I could use your feedback on how I can make this tool more useful for you. My focus areas for improvements:
- Portability across different platforms
- Memory optimization opportunities
- API design clarity and consistency
- Missing features you'd like to see
Feel free to open issues or PRs with suggestions!
The I/O adapter connects the CLI engine to your device’s communication interface
Adapters extend CliIoInterface to add platform-specific communication, while CliIoInterface provides higher level functionality (print, println, printf, etc.).
Using an existing adapter:
#include "mcli_arduino_serial.h"
ArduinoSerialIo io(Serial);Creating a custom adapter:
class MyIOAdapter : public mcli::CliIoInterface {
// Implement put_byte(), get_byte(), byte_available()
};The application context is a user-defined struct that holds references to all hardware interfaces, drivers, or other dependencies your CLI commands may need. This enables clean dependency injection, keeping commands modular and testable.
// app_context.h
struct MyAppContext {
MyIOAdapter& io; // Optional: include your IO adapter for in-function printing.
Gpio& gpio;
Sensor& temp_sensor;
// Add other peripherals or variables here
};The CLI operates on command functions that have the following signature:
void my_command(const mcli::CommandArgs args, MyAppContext* ctx);CommandArgs structure
struct CommandArgs {
int argc; // Number of arguments
char argv[MAX_ARGS][MAX_ARG_LENGTH]; // Argument strings
};Example commands:
#include "mcli.h"
void toggle_led(const mcli::CommandArgs args, MyAppContext* ctx) {
ctx->gpio.set(LED_ID, "ON");
}
void read_temperature(const mcli::CommandArgs args, MyAppContext* ctx) {
uint32_t temp_celsius = ctx->temp_sensor.read_celsius();
ctx->io.printf("Temperature: %lu°C\n", temp_celsius);
}
void print_args(const mcli::CommandArgs args, MyAppContext *ctx) {
ctx->io.println("Command Test Demo\n");
ctx->io.printf("argc: %d\n", args.argc);
ctx->io.println("argv: ");
for(int i=0; i<args.argc; i++)
{
ctx->io.printf("%s ",args.argv[i]);
}
}Define your command table
const mcli::CommandDefinition<MyAppContext> commands[] = {
{"led", toggle_led, "Toggle LED state"},
{"temp", read_temperature, "Read temperature sensor"},
{"args", print_args, "Print command arguments"}
};Arduino simple example
#include "mcli.h"
#include "mcli_arduino_serial.h"
// Initialize components
ArduinoSerialIo io(Serial);
Gpio gpio;
Sensor temp_sensor;
MyAppContext ctx{io, gpio};
// Create CLI engine with components and command table
mcli::CliEngine<MyAppContext> cli(io, ctx, commands);
void setup() {
Serial.begin(9600);
gpio.init();
temp_sensor.init();
}
void loop() {
cli.process_input(); // Call this regularly in your main loop
}That's it! Your CLI is ready with an automatic help command alongside all your custom commands.
include/mcli/
└──mcli.h # Unified CLI types, engine, interface
include/adapters/
├── mcli_arduino_serial.h # Arduino Stream-based adapter
├── mcli_esp32_uart.h # ESP32 UART adapter (uses FreeRTOS driver)
└── mcli_esp32_wifi_sta.h # ESP32 WiFi STA adapter (uses FreeRTOS driver)
help— Lists all available commands with descriptions
- Copy
mcli.hand desired adapters to your project - Add to include path
- Include in your code
git submodule add https://github.com/ryanfkeller/mcli.git libs/mcliScaling Estimates:
- Arduino Uno/Nano: Likely comfortable for 5-15 commands with basic functionality
- Arduino Mega: Can handle 20+ complex commands with plenty of headroom
- ESP32/similar: Memory usage becomes negligible compared to available resources
Defined in mcli.h:
| Constant | Description | Default |
|---|---|---|
MAX_ARGS |
Max number of CLI args | 5 |
MAX_ARG_LENGTH |
Max chars per argument | 16 |
CMD_BUFFER_SIZE |
Input buffer size | 128 |
DEFAULT_PROMPT |
CLI prompt string | mcli> |
MIT License — see LICENSE file for details
