Field Notes · Nº 001 · Maio 2026
Field Notes · 001 — Brushless / SimpleFOC

Waking a Dead Motor

A brushless motor I salvaged more than twenty years ago, a SimpleFOC board, and the first real step back into robotics.

by · edgy14 min read/simpleFOCBLDCESP32FOC
Date
2026 · 05 · 29
Bench
ESP32 · DRV8302
Sensor
AS5600 · I²C
Status
Spinning
§01

The motor in the drawer

There is a small brushless motor that has followed me through four apartments, three cities and one career change. I pulled it out of a broken VCR sometime around 2003, decided it was obviously going to power something important, and then put it in a drawer for two decades. I never threw it out. Engineers don't throw out motors.

This year I finally decided to make it spin — properly, under field-oriented control, not just buzzing on a battery. It was the cleanest possible excuse to get back into robotics after years away. One motor, one driver, one weekend.1

The plan was deliberately small. No robot yet. No legs, no wheels, no grand chassis. Just closed-loop velocity control on a single salvaged BLDC, and a serial monitor telling me the truth.

§02

Field-oriented control, briefly

A brushless motor is just three coils and a spinning magnet. The hard part is timing: you have to push current through the right coils at the right instant, relative to where the rotor actually is. Get it wrong and the motor cogs, heats up, and goes nowhere.

Field-oriented control (FOC) solves this elegantly. It transforms the three messy phase currents into two clean, rotor-aligned axes — d (flux) and q (torque) — runs simple PI controllers there, and transforms the result back into three phase voltages. You command torque directly and the math handles the commutation.

The loop runs thousands of times a second. Conceptually it looks like this:

FIG. 1 — The FOC control loop, one iteration

phase i

feedback

θ

θ

ω* target

PI velocity

Inv. Park · dq→αβ

SVPWM

3φ driver

BLDC

Clarke · abc→αβ

Park · αβ→dq

AS5600 angle

"You stop thinking about coils, and start thinking about torque."

SimpleFOC implements every box in that diagram for you.2 My job was mostly wiring, and telling it the truth about my hardware.

§03

Wiring it up

The bench is deliberately boring: an ESP32 for brains, a DRV8302 gate driver to push real current, and an AS5600 magnetic encoder glued to the shaft so the controller always knows the rotor angle. A diametric magnet on the shaft end, an I²C bus, and six PWM lines.

FIG. 2 — Wiring: brains → driver → motor
ESP32 6× PWM · I²C PWM ×6 DRV8302 gate driver 3φ A·B·C BLDC 11 PP · salvaged AS5600 encoder magnet I²C feedback → ESP32 12V PSU shared GND Vmot
FIG. 2Shared ground is non-negotiable. The encoder talks I²C; the driver takes six PWM gate signals.

One detail that cost me an evening: the encoder and the logic side must share a ground with the power stage, or the angle readings wander and FOC quietly falls apart.3

§04

First light

Here is the entire sketch that brought it to life. SimpleFOC keeps it almost embarrassingly short — link a sensor, link a driver, pick a control mode, and call initFOC().

foc_first_light.inocpp
#include <SimpleFOC.h>

// 11 pole-pairs, salvaged ~2003. Pole count found by experiment.
BLDCMotor motor = BLDCMotor(11);
BLDCDriver3PWM driver = BLDCDriver3PWM(25, 26, 27, 12);

// AS5600 magnetic encoder over I2C — the rotor's source of truth
MagneticSensorI2C sensor = MagneticSensorI2C(AS5600_I2C);

void setup() {
  Serial.begin(115200);

  sensor.init();
  motor.linkSensor(&sensor);

  driver.voltage_power_supply = 12;
  driver.init();
  motor.linkDriver(&driver);

  motor.controller = MotionControlType::velocity;
  motor.PID_velocity.P = 0.2f;     // tuned on the bench, by ear
  motor.PID_velocity.I = 2.0f;
  motor.voltage_limit   = 6;       // be gentle with a 20-year-old

  motor.useMonitoring(Serial);
  motor.init();
  motor.initFOC();                 // align sensor, find electrical zero
  Serial.println(F("ready."));
}

void loop() {
  motor.loopFOC();
  motor.move(6.28);                // ~1 rev/s
  motor.monitor();
}

The one parameter I had to discover was the pole-pair count — the motor came with no datasheet, no markings, nothing. So I guessed, watched it cog, and adjusted until initFOC() reported a clean alignment.

§05

Telemetry & first spin

Upload, open the serial monitor at 115200 baud, and hold your breath. This is the moment twenty years had been building toward:

/dev/ttyUSB0 — serial monitor115200 baud
> initFOC() MOT: Monitor enabled. MOT: Init MOT: Enable driver. MOT: Align sensor. MOT: sensor_direction==CW MOT: PP check: [ok] MOT: Zero elec. angle: 2.41 MOT: No current sense. MOT: [ok] Ready. ready. target = 6.28 rad/s | vel = 6.27

It spun on the first genuine try — and twenty years collapsed into one quiet click. Logging the velocity against a step command shows the PI controller settling in well under 200 ms with barely any overshoot:

SCOPE — velocity step responselive
ω (rad/s)t (norm) →

And because torque control is what FOC is really for, here is measured torque against commanded current — the satisfyingly straight line that means commutation is actually working:

SCOPE — torque vs currentlive
τ (N·m)i_q (A) →
§06

What's next

One motor spinning under closed-loop velocity is not a robot. But it is the single hardest primitive in robotics, and now I have it on the bench, reproducible, logged. The next entries write themselves: closed-loop position control, then two motors and a five-bar linkage, then — finally — a leg.

Twenty years is a long time to leave a motor in a drawer. It turns out it was just waiting for the right weekend.

End of file · 001

References & Links

  1. SimpleFOC — Arduino field-oriented control librarydocs.simplefoc.com
  2. DRV8302 three-phase gate driver — datasheetti.com · DRV8302
  3. AS5600 12-bit magnetic position sensorams-osram.com
  4. A primer on field-oriented control & the Park/Clarke transformstechnical note
  5. Nova Eletrônica — archived issues (the reason this looks like this)archive
  6. Finding pole-pair count without a datasheetbench note