SaeA-ICT
← Works
PRODUCT · 2026

BLDC Stator Winder Portal

Four BLDC axes over a single USB port — UMCP binary protocol on top of FDCAN, 100 Hz telemetry, with the Three.js 3D simulator and the real machine sharing the same sequencer.

A full stack for running a small BLDC stator-winding cell from one PC. We built the motor-driver firmware, the FDCAN hub firmware, the proprietary binary protocol on top of USB, and the browser GUI ourselves. The default geometry is 24-slot 22-pole, but other patterns drop in as a one-line config.

 

Product overview

• Target motor: 24-slot 22-pole BLDC (other geometries via the winding_config string)

• Four axes: M0 Linear Rail · M1 Slot Indexer · M2 Winding Motor · M3 Tension Control (torque mode)

• PC side: a single browser page with four tabs — Node Management / Motor Tuning / 3D Simulation / Live Winding

• Inspired by aotenjo-xyz/winder, but the firmware, protocol, and UI are all rewritten from scratch.

 

Hardware / firmware stack

• Four motor-driver boards: Aotenjo One v2.0+ (STM32G431CB @ 170 MHz, DRV8313, 2.5 A peak, 8–35 V, 14-bit magnetic encoder)

• One FDCAN hub board: Aotenjo Master v2.0+ (STM32G431CB, USB-C, VCC sensing)

• Motor firmware: SimpleFOC closed-loop FOC, in three modes — Position / Velocity / Torque

• Hub firmware: a transparent USB↔FDCAN bridge. It does not parse payloads — only validates the link, samples CAN-bus utilisation, and reads VBUS.

• Protocol: UMCP (U-MCP), a custom binary protocol (referenced ST MCSDK), with byte stuffing and SOF/ESC framing. Recommended USB CDC at 12 Mbps; the default in code is 2 Mbps.

 

FDCAN / UMCP command set

CAN ID is encoded as can_id = (func_code << 7) | node_idnode_id=0 is broadcast, 1–4 are the motors.

NMT (0x0) — ESTOP / ESTOP_CLEAR / RESET

CMD_REALTIME (0x2) — 18 B: mode + state + target + limit + iq + id (FF)

RESP_REALTIME (0x3) — 20 B at 100 Hz: angle, vel, sens, torque_vq, vbus

SDO (0x5 / 0x6) — 8 B: single-parameter read/write (Vel_P/I/D, Ang_P, Current_Limit, …)

BULK_PARAM (0x8) — 48 B: 12 × float bulk read/write

ID_SET / ID_REPORT (0x10 / 0x11) — node assignment based on the STM32 96-bit UID

 

Web GUI — Flask + vanilla JS + Three.js

Python deps are just flask, pyserial, pyyaml. The whole UI is one page at http://localhost:5000, with four tabs:

Node Management — pressing PB1 on a driver board ships its STM32 UID over serial. Map a Node No (1–4) to that UID, hit SET, and a single ALL APPLY restores every node ID and PID set after a reboot.

Motor Tuning — SDO/BULK read/write for PID, current, and velocity limits. Per-node JSON snapshots in config/motor_params_N.json. Both RAD and DEG units are supported, with a guard rail because the wrong unit can spin the motor up fast enough to burn the DRV8313.

Winding Simulation — Three.js renders the stator and the winding arm in real time. The motor physics use the same PID values as the live machine, so the simulation tracks reality. A virtual USB port (PTY) lets SerialManager talk to the sim engine just like it would to hardware.

Stator Winder — connected to real hardware. Hand-rotate the winding arm and the 3D view follows live (telemetry is round-tripping). W0/W1/W2 wind a single phase, Auto runs the full sequence.

 

Settings — settings.yml

The winding pattern is just a string. The default 24-slot 22-pole pattern is

winding_config: "AaAabBbBCcCcaAaABbBbcCcC", turns_per_slot: 75.

M0 Linear Rail — wind_range_start / end, end_to_zero, velocity

M1 Slot Indexer — zero offset, velocity

M2 Winding Motor — zero, angle_to_prevent_collision, velocity

M3 Tension Control — pull_wire_torque / wind_torque (torque mode)

 

Key design decisions

ASCII → binary — the line-by-line ASCII protocol common in off-the-shelf winders was replaced with UMCP (SOF + ESC byte stuffing). 100 Hz telemetry and SDO / BULK parameter traffic share the same USB without contention.

The hub stays dumb — the master firmware never parses payloads, just forwards them. New commands ship from the PC side without re-flashing the hub.

A path to verify without hardware — virtual USB (PTY) + SimpleFOC physics let us validate the sequencer, the UI, and the protocol end-to-end without a live machine.

UID-based node IDs — boards can be swapped or re-flashed and a single ALL APPLY restores the full node map and per-node PID set.