STM32-Serial-Shell

STM32 Serial Shell - CLI with JSON Support

Project Website

An interactive command-line interface for STM32 that processes commands over UART. Built to understand embedded parsers and driver architecture.


Why This Exists

After building Serial-JSON-Bridge, I needed a way to interact with embedded systems during development. Instead of hardcoding test commands or reflashing firmware for every debug change, I built a proper CLI that lets me configure, test, and debug hardware interactively through a serial terminal.

This is the foundation for building more complex embedded tools—like remote device management, diagnostic interfaces, or even the command interface for the HTTP-Ethernet-Assistant project.

The Goal: Build a reusable CLI framework that works across projects without modifying core code.


What It Does

Type commands in a serial terminal → STM32 processes them → Executes actions → Responds back

Example Commands:

help
set led on
get status

Can also accept JSON format for structured commands.


System Flow

graph LR
    A[Terminal] -->|UART RX| B[STM32<br/>Interrupt]
    B --> C[CLI Parser<br/>Command Split]
    C --> D[Command<br/>Handler]
    D -->|UART TX| E[Response<br/>Terminal]
    
    style A fill:#e1f5e1
    style C fill:#e1e5ff
    style D fill:#ffe1f0
    style E fill:#e1f5e1

Flow Details:

  1. UART receives data byte-by-byte via interrupts
  2. CLI parser extracts command and parameters
  3. Registered handler executes the command
  4. Response formatted and sent back via UART

Architecture

Clean 3-Layer Design:


Key Features

✅ Interrupt-driven UART (non-blocking)
✅ Command registration system
✅ Built-in commands: help, set, get
✅ JSON parsing support with JSMN
✅ Zero dynamic memory allocation
✅ Error detection & recovery


Quick Start

Hardware: STM32G0 + USB-UART adapter

# Clone repo
git clone https://github.com/BlackWiz/STM32-Serial-Shell.git

# Build and flash to STM32
# Connect serial terminal
screen /dev/ttyUSB0 9600

Try it:

> help
> set test 123
> get test

Adding Your Command

// Define handler
base_type my_cmd_handler(char *out, size_t len, const char *cmd) {
    sprintf(out, "Custom response\r\n");
    return CLI_FALSE;
}

// Define command
const cli_command_definition_t my_cmd = {
    "mycmd",
    "My custom command",
    my_cmd_handler,
    0  // parameter count
};

// Register in main
cli_register_command(&my_cmd);

⚔️ The Hard Parts

Edge Case Handling: Users type incomplete commands, send garbage data, or mash enter repeatedly. The parser needed to handle all of this without crashing or corrupting state. Spent significant time building robust input validation and buffer boundary checking.

Command Registration Design: Building an extensible system where commands can be added without modifying core CLI code. Took several iterations to get the function pointer design right—balancing flexibility with memory constraints on STM32G0.

Buffer Management: Balancing buffer sizes—too small and you lose data during rapid typing, too large and you waste precious RAM. Ended up profiling actual user input patterns to size buffers appropriately.

Concurrent TX/RX: UART transmit and receive happening simultaneously via interrupts. Had to ensure the CLI could respond to commands while still accepting new input without data corruption.


Performance & Testing

Memory Footprint:

Tested on real hardware with edge cases:


Technical Notes

UART State Machine:

Memory Safety:

Command Parser:


📚 Key Learnings

Extensibility vs Simplicity: The command registration system uses function pointers and structs—more complex than a giant switch statement, but infinitely more maintainable when you have 10+ commands.

User Input is Chaos: Never assume users will type correctly. Built the parser to gracefully handle every edge case: empty input, too-long commands, special characters, rapid typing, partial commands.

Building Reusable Frameworks: This CLI code now works across multiple projects (Serial-JSON-Bridge, HTTP-Ethernet-Assistant) without modification—just register different commands. That’s the power of good abstraction.

Integration with Serial-JSON-Bridge: This project extends the UART driver from Serial-JSON-Bridge, demonstrating how to build layered embedded systems—driver at the bottom, application framework on top.


This project builds on concepts from:


**STM32G0 UART (9600 baud) Bare-metal C No simulation lies**

Built by Sri Hari Shankar Sharma. Foundation for interactive embedded systems development.