# Serial Commander

# High Level Overview

## Purpose

The Serial Commander is a terminal user interface (TUI) for sending commands to an STM32 CAN board over a serial connection. It provides a live split-screen view of outgoing commands and incoming serial data, with editable motor and PIO parameters.

## Prerequisites

The STM32 firmware must be built with serial command reception enabled. The firmware must parse newline-terminated ASCII strings from UART/USB-CDC and handle the following command tokens:

- `<span class="editor-theme-code">speed <erpm></span>`
- `<span class="editor-theme-code">stop</span>`
- `<span class="editor-theme-code">profile <erpm> <accel> <time_ms></span>`

Without this firmware support the TUI will open and display incoming data, but sent commands will have no effect aside form rebuilding and flashing the firmware

## Core Responsibilities

The tool handles three concerns in one interface:

- **Command dispatch**<span style="white-space: pre-wrap;"> — sends formatted serial strings (</span>`<span class="editor-theme-code">speed</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">stop</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">profile</span>`) to the STM32 on keypress
- **Live serial monitoring**<span style="white-space: pre-wrap;"> — streams and colour-codes all incoming serial output in real time</span>
- **PIO integration**<span style="white-space: pre-wrap;"> — triggers PlatformIO build/flash operations without leaving the terminal</span>

## Architecture

Three concurrent components run during a session:

**Main TUI Loop**<span style="white-space: pre-wrap;"> — Draws the interface at 100 ms intervals, handles keypresses, and dispatches commands or parameter edits. Runs on the main thread via </span>`<span class="editor-theme-code">curses.wrapper</span>`.

**Serial Reader Thread**<span style="white-space: pre-wrap;"> — Daemon thread that continuously calls </span>`<span class="editor-theme-code">ser.readline()</span>`<span style="white-space: pre-wrap;"> and appends decoded lines to a shared </span>`<span class="editor-theme-code">rx_log</span>`<span style="white-space: pre-wrap;"> deque. Terminates automatically when the serial connection drops.</span>

**PIO Runner Thread**<span style="white-space: pre-wrap;"> — Spawned on demand when the user triggers a build. Runs </span>`<span class="editor-theme-code">pio run -t <target> -e <env></span>`<span style="white-space: pre-wrap;"> as a subprocess and streams stdout into </span>`<span class="editor-theme-code">rx_log</span>`. Does not block the TUI.

<span style="white-space: pre-wrap;">All three components share </span>`<span class="editor-theme-code">rx_log</span>`<span style="white-space: pre-wrap;"> and </span>`<span class="editor-theme-code">tx_log</span>`<span style="white-space: pre-wrap;"> as </span>`<span class="editor-theme-code">collections.deque(maxlen=200)</span>`<span style="white-space: pre-wrap;"> — thread-safe for the single-producer/single-consumer append and iteration patterns used here.</span>

# Interface Layout

## Layout

The TUI divides the terminal into four regions separated by line-drawing characters.

```
┌─────────────────────────────────────────────────────────────────┐
│  STM32 SERIAL COMMANDER              /dev/ttyACM0  ·  115200 baud │
├──────────────────────────────┬──────────────────────────────────┤
│  COMMANDS                    │  MOTOR                           │
│                              │  erpm    2000  [e]               │
│  [1] speed                   │  accel   1000  [a]               │
│  [2] stop                    │  time ms 5000  [t]               │
│  [3] profile                 │                                  │
│                              │  PIO                             │
│  [b] pio build               │  target  upload      [p]         │
│                              │  env     can_board   [n]         │
│  [q] quit                    │                                  │
├──────────────────────────────┼──────────────────────────────────┤
│  TX                          │  INCOMING                        │
│  ▸ connected /dev/ttyACM0    │  [INFO] system ready             │
│  ▸ speed 2000                │  [INFO] CAN init ok              │
│  ▸ profile 2000 1000 5000    │  [WARNING] temp high             │
├──────────────────────────────┴──────────────────────────────────┤
│  1 speed   2 stop   3 profile   b pio build   e/a/t/p/n edit   q quit │
└─────────────────────────────────────────────────────────────────┘
```

## Regions

**Top-left — COMMANDS**<span style="white-space: pre-wrap;">: Available key bindings. </span>`<span class="editor-theme-code">[q]</span>`<span style="white-space: pre-wrap;"> is visually separated from the command group.</span>

**Top-right — MOTOR / PIO**: Editable parameters with current values. Each shows its edit key in brackets. Values highlight in yellow when active.

**Bottom-left — TX**<span style="white-space: pre-wrap;">: Scrolling log of sent commands, prefixed with </span>`<span class="editor-theme-code">▸</span>`. Most recent entries appear at the bottom.

**Bottom-right — INCOMING**: Scrolling serial output, colour-coded by severity:

<table id="bkmrk-prefixcolour%5Berror%5Dr"><colgroup><col></col><col></col></colgroup><tbody><tr><th>Prefix

</th><th>Colour

</th></tr><tr><td>`<span class="editor-theme-code">[ERROR]</span>`

</td><td>Red

</td></tr><tr><td>`<span class="editor-theme-code">[WARNING]</span>`

<span style="white-space: pre-wrap;"> / </span>

`<span class="editor-theme-code">[WARN]</span>`

</td><td>Yellow

</td></tr><tr><td>`<span class="editor-theme-code">[INFO]</span>`

</td><td>Cyan

</td></tr><tr><td>**(other)**

</td><td>White

</td></tr></tbody></table>

**Status bar**: Shows edit mode prompt when a field is being edited; otherwise shows the full key reference.

## Terminal Requirements

<span style="white-space: pre-wrap;">Minimum ~80×24 terminal. Colour support recommended — falls back to </span>`<span class="editor-theme-code">curses.COLOR_MAGENTA</span>`<span style="white-space: pre-wrap;"> if the terminal cannot redefine colours (the TUI uses a custom purple </span>`<span class="editor-theme-code">#5a2273</span>`).

# Commands and Keybindings

## Serial Commands

<table id="bkmrk-keycommand-sentdescr"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th>Key

</th><th>Command sent

</th><th>Description

</th></tr><tr><td>`<span class="editor-theme-code">1</span>`

</td><td>`<span class="editor-theme-code">speed <erpm></span>`

</td><td>Sets motor speed to current erpm value

</td></tr><tr><td>`<span class="editor-theme-code">2</span>`

</td><td>`<span class="editor-theme-code">stop</span>`

</td><td>Sends stop command

</td></tr><tr><td>`<span class="editor-theme-code">3</span>`

</td><td>`<span class="editor-theme-code">profile <erpm> <max_accel> <time_ms></span>`

</td><td>Sends full motion profile

</td></tr><tr><td>`<span class="editor-theme-code">b</span>`

</td><td>**(PIO subprocess)**

</td><td><span style="white-space: pre-wrap;">Runs </span>

`<span class="editor-theme-code">pio run -t <target> -e <env></span>`

<span style="white-space: pre-wrap;"> in background</span>

</td></tr><tr><td>`<span class="editor-theme-code">q</span>`

</td><td>**(none)**

</td><td>Quits the TUI and closes the serial port

</td></tr></tbody></table>

## Parameter Edit Keys

<table id="bkmrk-keyfieldvalidationee"><colgroup><col></col><col></col><col></col></colgroup><tbody><tr><th>Key

</th><th>Field

</th><th>Validation

</th></tr><tr><td>`<span class="editor-theme-code">e</span>`

</td><td>erpm

</td><td>Must be integer (negative allowed)

</td></tr><tr><td>`<span class="editor-theme-code">a</span>`

</td><td>accel

</td><td>Must be integer (negative allowed)

</td></tr><tr><td>`<span class="editor-theme-code">t</span>`

</td><td>time\_ms

</td><td>Must be integer (negative allowed)

</td></tr><tr><td>`<span class="editor-theme-code">p</span>`

</td><td>pio target

</td><td>Any non-empty string

</td></tr><tr><td>`<span class="editor-theme-code">n</span>`

</td><td>pio env

</td><td>Any non-empty string

</td></tr></tbody></table>

## Edit Mode Behaviour

<span style="white-space: pre-wrap;">Pressing an edit key switches the status bar to an edit prompt and opens an inline text field on the parameter row. </span>**Enter**<span style="white-space: pre-wrap;"> confirms; </span>**ESC**<span style="white-space: pre-wrap;"> restores the previous value. Invalid integer inputs are rejected and logged to the TX log with a </span>`<span class="editor-theme-code">!!</span>`<span style="white-space: pre-wrap;"> prefix — the parameter retains its prior value.</span>

## PIO Build

<span style="white-space: pre-wrap;">Pressing </span>`<span class="editor-theme-code">b</span>`<span style="white-space: pre-wrap;"> spawns a background thread running:</span>

```
pio run -t <pio_target> -e <pio_env>
```

<span style="white-space: pre-wrap;">Output streams line-by-line into the INCOMING panel. </span>`<span class="editor-theme-code">[INFO] PIO: done (exit 0)</span>`<span style="white-space: pre-wrap;"> appears on success. </span>`<span class="editor-theme-code">[ERROR] PIO: 'pio' not found in PATH</span>`<span style="white-space: pre-wrap;"> appears if PlatformIO is not installed. </span>

\--- Page 4 — Usage

<span style="white-space: pre-wrap;">Output streams line-by-line into the INCOMING panel. </span>`<span class="editor-theme-code">[INFO] PIO: done (exit 0)</span>`<span style="white-space: pre-wrap;"> appears on success. </span>`<span class="editor-theme-code">[ERROR] PIO: 'pio' not found in PATH</span>`<span style="white-space: pre-wrap;"> appears if PlatformIO is not installed.</span>

# Usage

## Dependencies

```
pip install pyserial
```

PlatformIO (`<span class="editor-theme-code">pio</span>`<span style="white-space: pre-wrap;">) must be in </span>`<span class="editor-theme-code">PATH</span>`<span style="white-space: pre-wrap;"> for build/flash functionality.</span>

## Running

<span style="white-space: pre-wrap;">Auto-detect STM32 device (VID </span>`<span class="editor-theme-code">0x0483</span>`):

```
python3 serial_commander.py
```

Specify port and baud rate manually:

```
python3 serial_commander.py --port /dev/ttyACM0 --baud 115200
```

If multiple STM32 devices are connected, the script presents a numbered selection menu before opening the TUI.

## Default Parameter Values

<table id="bkmrk-parameterdefaulterpm"><colgroup><col></col><col></col></colgroup><tbody><tr><th>Parameter

</th><th>Default

</th></tr><tr><td>erpm

</td><td>2000

</td></tr><tr><td>accel

</td><td>1000

</td></tr><tr><td>time\_ms

</td><td>5000

</td></tr><tr><td>pio target

</td><td>upload

</td></tr><tr><td>pio env

</td><td>can\_board

</td></tr></tbody></table>

## Typical Workflow

1. Connect STM32 CAN board via USB.
2. <span style="white-space: pre-wrap;">Run </span>`<span class="editor-theme-code">python3 serial_commander.py</span>`<span style="white-space: pre-wrap;"> — port is detected automatically.</span>
3. <span style="white-space: pre-wrap;">Adjust </span>`<span class="editor-theme-code">erpm</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">accel</span>`<span style="white-space: pre-wrap;">, </span>`<span class="editor-theme-code">time_ms</span>`<span style="white-space: pre-wrap;"> with </span>`<span class="editor-theme-code">e</span>`/`<span class="editor-theme-code">a</span>`/`<span class="editor-theme-code">t</span>`<span style="white-space: pre-wrap;"> as needed.</span>
4. <span style="white-space: pre-wrap;">Send </span>`<span class="editor-theme-code">speed</span>`<span style="white-space: pre-wrap;"> (</span>`<span class="editor-theme-code">1</span>`<span style="white-space: pre-wrap;">) or </span>`<span class="editor-theme-code">profile</span>`<span style="white-space: pre-wrap;"> (</span>`<span class="editor-theme-code">3</span>`) and observe STM32 response in INCOMING.
5. <span style="white-space: pre-wrap;">Use </span>`<span class="editor-theme-code">b</span>`<span style="white-space: pre-wrap;"> to build/flash directly if firmware changes are needed.</span>
6. <span style="white-space: pre-wrap;">Press </span>`<span class="editor-theme-code">q</span>`<span style="white-space: pre-wrap;"> to quit — serial port closes cleanly.</span>