plate-state // Hardware State for Plates
/
Lunch

What is plate-state

plate-state gives a Plate's browser UI access to hardware over serial. It builds a state model from serial traffic and serves it to the Plate Console tab via WebSocket. It can also send commands.

stm32-mcp is completely independent. It works on its own, as it always has. plate-state does not sit between Claude and hardware. It comes along for the ride.

Serial Access

plate-state has two modes for connecting to a serial port:

Join an stm32-mcp session

When stm32-mcp is running, it starts a serial bridge (TCP server on localhost:8765) that multiplexes serial access. The stm32-serial CLI already connects to this bridge as an interactive terminal. plate-state connects the same way: as another client of the existing bridge. It sees all traffic and can send commands through the shared, lock-protected connection.

graph TD
  MCP["stm32-mcp"] -->|starts| Bridge["Serial Bridge\nlocalhost:8765"]
  Bridge <-->|pyserial| Port["Serial Port\n/dev/tty*"]
  Bridge <--> CLI["stm32-serial\n(terminal)"]
  Bridge <--> PS["plate-state"]
  PS <-->|WebSocket :9147| Console["Plate Console\n(browser)"]
        

Open the port directly

When stm32-mcp is not running, plate-state opens the serial port itself. No bridge needed. You're just using the Plate Console to talk to hardware on your own.

Either way, the Plate Console gets the same interface. The connection mode is an implementation detail.

State Model

plate-state watches serial traffic and builds a model of what it knows about the hardware. When traffic comes through (from any source), plate-state parses responses and updates the model. The Plate Console renders the current model. Widgets stay in sync with reality.

EventWhat happens
Slider dragged in Plate Consoleplate-state sends command via bridge (or directly). Hardware responds. Model updates. Widget confirms.
Claude sends a command via stm32-mcpTraffic flows through bridge. plate-state observes it. Model updates. Plate Console reflects the change.
Hardware sends unsolicited dataplate-state sees it on the wire, parses it, updates model, notifies the Console.
Plate Console connectsplate-state sends current state snapshot. No stale UI.

Parsing responses into the state model is project-specific. Each firmware speaks its own protocol. plate-state needs a parser per project, or a generic line-based approach that the Plate Console interprets.

WebSocket Protocol

plate-state serves the Plate Console over WebSocket. JSON messages, three types:

DirectionTypeShape
Console → plate-statecommand{ type: "command", cmd: "REG_READ 0x04" }
plate-state → Consoletraffic{ type: "traffic", dir: "tx"|"rx", data: "...", ts: ... }
plate-state → Consolestate{ type: "state", model: {...}, connected: true }

Console Tab

The Console is a Plate tab that connects to plate-state. Two panes: traffic on the left, widgets on the right.

Serial Traffic
14:22:03 → REG_READ 0x00
14:22:03 ← 0x1A [EN_A=1 EN_B=1]
14:22:08 → DIM_A 128
14:22:08 ← OK CH_A=128 (50.2%)
14:22:15 → REG_READ 0x04
14:22:15 ← 0x38 [No faults]
Enter command...
Send
Widgets
Channel A
Level
128
Send
Faults
0x38 : None
Read Clear
Quick Commands
En A En B Disable All

Traffic Pane

ClassPrefixColorUse
txBlueCommands sent to hardware
rxGreenResponses from hardware
sysitalicDimConnection events
err!RedErrors, faults, timeouts

Widget Types

TypeControlsUse
SliderRange + number input + SendAnalog parameters (brightness, current set)
ButtonsLabeled button groupDiscrete commands (enable, disable, reset)
StatusRead-only display + action buttonsRegister readback, fault status

Widgets are defined in HTML or a static JSON file, not a database. They change when the firmware interface changes.

Connection States

StateIndicatorBehavior
plate-state not runningGray dotWidgets disabled, command input disabled
Connected, no serial portYellow dotplate-state is up but no port open
Connected, port openGreen dotFull operation

Implementation

StepWhat
1plate-state process. Connects to serial bridge (or opens port directly). WebSocket server on :9147.
2Traffic forwarding. Serial data flows to Console via WebSocket. Console commands flow to serial.
3State model. Parse responses, build model, broadcast to Console on change.
4Console tab in Plate. Traffic pane, command input, connection status.
5Widget panel. Render widget definitions, wire controls to commands, update from state.

Open Questions

How should plate-state decide between bridge mode and direct mode? Auto-detect (try bridge first, fall back to direct)? A flag? TBD.

The state model parser is project-specific. Is there a generic approach (line-based, key:value) that covers most firmware protocols, or does each project need its own parser?