If you work with stepper motors in robotics, CNC, or automation, you have probably experienced missed steps — that moment when your open-loop stepper fails to execute a commanded move because of binding, sudden load, or overly aggressive acceleration profiles. The fix? Add position feedback.
This guide walks through retrofitting a standard NEMA 17 stepper motor with a magnetic encoder, step by step. We use Grafito's CANStepper controller board as the example platform, but the mechanical and electrical principles apply to any encoder-based retrofit.
Why Retrofit Instead of Replacing?
Buying a new closed-loop stepper motor out of the box is one path. Retrofitting your existing motor is another — and often the better choice when:
- You already have motors mounted in your machine and don't want to redesign the bracket geometry.
- Your NEMA 17 motors are high-quality units you trust (good bearings, known torque curves).
- You want to add closed-loop capability without discarding working hardware.
- You need a path to gradually upgrade — retrofit one axis at a time.
The core idea: mount a magnetic encoder behind the motor's rear shaft, let a controller board read absolute angle in real time, and close the position loop in firmware.
What You Need
Here is the bill of materials for one retrofit:
| Item | Notes |
|---|---|
| NEMA 17 stepper motor | Must have a rear shaft (dual-shaft) or accessible rear bearing face. Single-shaft motors with a flat rear end can work with adhesive-mounted magnets. |
| Magnetic rotary encoder | MT6701 (14-bit, SPI/I2C) is the recommended choice. It offers 0.022° resolution and a wide air-gap tolerance. |
| Diametrically magnetised disc magnet | Ø6 mm × 2.5 mm is standard. Must be diametrically polarised (N-S across the diameter, not through the thickness). |
| Mounting bracket or adapter | Holds the encoder PCB at a fixed distance from the magnet. CANStepper includes an integrated bracket. For DIY builds, 3D-print a spacer ring. |
| Controller board | We use the CANStepper (ESP32-C3 + TMC2209 + MT6701). Alternatives: any MCU with SPI/I2C + a stepper driver. |
| Power supply | 5–24 V DC, rated for your motor's current draw plus controller overhead. |
| USB-C cable | For flashing firmware and serial monitoring. |
Step 1: Mechanical Mounting
Access the Rear Shaft
If your NEMA 17 has a dual shaft (shaft protruding from both ends), the rear shaft is your magnet mounting surface. Clean it with isopropyl alcohol first.
If your motor is single-shaft, the rear face is typically a flat bearing cap. You can bond the magnet directly to the centre of this face — but alignment becomes more critical, and you lose the self-centring benefit of a shaft.
Magnet Alignment
The magnet must be centred over the encoder IC and sit within the specified air gap:
- Gap range: 0.5–2.0 mm between magnet face and encoder chip surface.
- Too close (< 0.3 mm) risks mechanical contact if the shaft has any axial play.
- Too far (> 2.5 mm) degrades signal-to-noise ratio and can cause angle jitter.
Attachment options:
| Method | Best for |
|---|---|
| Press-fit onto rear shaft | Dual-shaft motors — fast, reliable, no cure time. |
| Cyanoacrylate (super glue) | Quick fix; can become brittle under vibration. |
| Epoxy (two-part) | Strongest bond; recommended for production use. |
If gluing, apply a tiny amount to the magnet centre only — avoid squeeze-out onto the encoder PCB.
Mount the Encoder Board
The encoder PCB must be rigidly fixed relative to the motor body. CANStepper uses a PCB that sandwiches between the motor and the mounting surface, with a cutout that aligns the MT6701 over the rear shaft. If you are fabricating your own bracket:
- Measure the motor's bolt pattern (standard NEMA 17: 31 mm × 31 mm, M3 screws).
- Design a spacer that stands the PCB off the motor by the correct distance.
- Use nylon standoffs to avoid shorting the PCB to the motor housing.
Step 2: Magnet Calibration
Once mechanically mounted, calibrate the encoder before trusting its readings.
CANStepper Calibration Routine
On CANStepper, calibration is built into the firmware. Connect via USB-C serial (115200 baud) and send:
$CAL
The firmware will:
- Spin the motor slowly through several full rotations.
- Record the raw magnetic field vectors (X, Y components).
- Compute gain and offset corrections for each axis.
- Store the calibration in non-volatile memory.
After calibration, verify the readout:
$ANGLE
Rotate the shaft by hand. You should see a smooth 0–360° output with no jumps, dead spots, or wraparound glitches. If you see jitter beyond ±0.1°, re-check the air gap and magnet centring.
DIY Calibration (Without CANStepper)
If you are using a raw MT6701 over I2C/SPI with your own MCU:
- Read raw angle at a known rate (e.g., 1 kHz).
- Spin the motor at a constant speed (use another motor or a drill).
- Collect a full-revolution dataset of raw readings.
- Fit an elliptical correction: the raw X-Y plot of magnetic field vectors forms an off-centre ellipse. Scale and shift it to a unit circle.
- Apply the correction as
atan2(corrected_Y, corrected_X).
This is doable but tedious — CANStepper's built-in calibration saves significant time.
Step 3: Electrical Connections
Motor Phase Wiring
NEMA 17 steppers have two phases (A and B), each with a centre-tapped coil. Wire the full coil (end-to-end) for maximum torque:
| Motor wire colour (typical) | Phase | CANStepper terminal |
|---|---|---|
| Black | A+ | A1 |
| Green | A- | A2 |
| Red | B+ | B1 |
| Blue | B- | B2 |
Note: Wire colour conventions vary by manufacturer. Use a multimeter in continuity mode to identify coil pairs: low resistance (~1–5 Ω) between A+ and A-, same for B+ and B-. Infinite resistance between A and B wires.
Encoder Connection
On CANStepper, the MT6701 is already integrated on the PCB — no external wiring needed. If you are wiring a standalone MT6701 breakout:
MT6701 → MCU
VCC → 3.3V
GND → GND
SDA → GPIO (I2C data)
SCL → GPIO (I2C clock)
Add 4.7 kΩ pull-up resistors on SDA and SCL if your MCU doesn't have internal pull-ups.
Power Supply
CANStepper accepts 5–24 V DC on the main power input. Choose a supply based on your motor's rated voltage:
- For a 2.8 V / 1.2 A NEMA 17: a 12 V supply gives good high-speed performance.
- For a 3.6 V / 1.7 A NEMA 17: a 24 V supply improves torque at speed.
The TMC2209 driver regulates current internally, so the supply voltage does not set the motor current — it only affects the maximum speed before back-EMF limits torque.
Step 4: Configure Motor Current
Setting the correct RMS current is critical. Too low and you lose torque. Too high and the motor overheats.
Find Your Motor's Rated Current
Check the motor datasheet. Common NEMA 17 ratings:
| Motor length | Typical rated current (RMS) |
|---|---|
| 34 mm | 1.0 A |
| 40 mm | 1.2 A |
| 48 mm | 1.5–1.7 A |
| 60 mm | 2.0 A |
Set Current on CANStepper
CANStepper uses the TMC2209 in StealthChop2 mode with software-configurable RMS current. Send:
$CURR 1200
This sets 1200 mA RMS (1.2 A). The TMC2209 automatically scales this to the appropriate peak current internally.
Validation: After setting current, run the motor at 60 RPM under load for 10 minutes. The motor case should be warm but not too hot to touch (max ~80 °C on the case is acceptable for most NEMA 17 steppers). If it's scorching, reduce current by 100 mA and retest.
Step 5: Flash Firmware
CANStepper Firmware
CANStepper ships with closed-loop firmware pre-flashed. To update or re-flash:
# Using esptool.py
esptool.py --chip esp32c3 --port /dev/ttyUSB0 --baud 921600 write_flash \
0x0 canstepper-firmware.bin
Find the latest firmware at docs.grafito.in.
Verify Encoder Data
After flashing, open a serial terminal:
screen /dev/ttyUSB0 115200
Send $STATUS. You should see:
MODE:CLOSED_LOOP POS:0.00 ERR:0.00 TEMP:32.4 CURR:0.00
Rotate the shaft by hand. The POS field should track the movement. If it stays at 0.00 or jumps erratically, revisit Step 2 (calibration) and check the magnet gap.
Step 6: Closed-Loop Test
Now the real test — does the loop actually close?
Basic Position Command
Send an absolute move command:
$GOTO 90.0
The motor should rotate to 90°. Read back:
$WHERE
Expected: 90.00 ± 0.1°. If it overshoots and oscillates, the position-loop gains need tuning (Step 7).
Load Test
- Command a move to 180°.
- While holding position, apply a gentle load with your hand (try to back-drive the shaft).
- Monitor the error with
$ERR.
A well-tuned loop should resist the load, detect the position error, and drive current to restore position — all within a few hundred milliseconds. The error should settle back to near zero.
Repeatability Test
Run a sequence:
$GOTO 0
$GOTO 360
$GOTO 0
$GOTO 360
After 10 cycles, check that the shaft returns to the same physical position each time. A repeatability of ±0.05° is achievable with the MT6701.
Step 7: Tuning
Closed-loop performance depends on three gain terms.
PID Gains
| Term | Effect | Typical CANStepper default |
|---|---|---|
| P (proportional) | Stiffness — how hard the motor pushes back against position error. | 8.0 |
| I (integral) | Eliminates steady-state error over time. Too high causes slow oscillation. | 0.5 |
| D (derivative) | Damping — resists rapid changes, reduces overshoot. | 2.0 |
Tuning process:
- Start with P only (I=0, D=0). Increase P until the motor oscillates (audible buzz), then back off by 30%.
- Add D to damp the oscillation. Increase until the motor feels "stiff but quiet."
- Add I to eliminate any residual steady-state error after a move completes.
Send gains via serial:
$PID 8.0 0.5 2.0
Velocity Feedforward
Feedforward predicts the required current for a target velocity, reducing the burden on the feedback loop. CANStepper exposes this as:
$VFF 0.15
A good starting value is 0.10–0.20. Too high causes overshoot on deceleration; too low and the motor lags during constant-velocity segments.
Trapezoidal Velocity Profiles
For point-to-point moves, a trapezoidal profile (accelerate → cruise → decelerate) minimises jerk and missed steps:
$PROFILE TRAPEZOID
$ACCEL 500 # deg/s²
$VEL 300 # deg/s cruise
$GOTO 720.0 # two full revolutions
The motor should accelerate smoothly, hold speed, then decelerate to a clean stop — no audible knocking or oscillation at the endpoints.
Step 8: Limitations
Closed-loop retrofitting is powerful, but it has real-world constraints:
Speed Under Load
A NEMA 17 running closed-loop at 24 V can typically hold torque up to ~300 RPM. Beyond that, back-EMF reduces available current, and the loop may not keep up with rapid load changes. For high-speed applications (> 1000 RPM), consider a NEMA 23 or a dedicated servo motor.
NEMA 17 Only
The CANStepper form factor is designed for NEMA 17 (42 mm frame) motors. It does not fit NEMA 14, NEMA 23, or larger frames without mechanical modification.
GCSP Protocol, Not CANopen
CANStepper uses GCSP (Grafito Closed-Loop Stepper Protocol), a lightweight CAN protocol designed for simplicity and low overhead. It is not CANopen or CiA 402 compatible. If your system requires standards-compliant CANopen, verify protocol compatibility before committing.
Encoder Resolution
The MT6701 provides 14-bit resolution (16,384 counts/rev). This is excellent for most applications, but high-precision metrology or sub-arcminute positioning may need optical encoders with higher line counts.
Next Steps
Once your first motor is running closed-loop:
- Add a second motor and test multi-axis coordination.
- Connect via CAN Bus — daisy-chain up to 16 nodes on a single twisted pair. CANStepper auto-negotiates node IDs.
- Integrate limit switches for homing routines.
- Explore the GCSP command set at docs.grafito.in — absolute/relative moves, velocity mode, position streaming, and diagnostics.
Resources
- CANStepper product page: grafito.in/shop/products/canstepper-adapter-board
- Documentation: docs.grafito.in
- Firmware releases: Available through the docs site under the CANStepper section.
- Related guide: How to Make a NEMA 17 Stepper Motor Closed Loop — broader comparison of DIY, serial module, and integrated CAN approaches.
Retrofitting encoder feedback onto your existing NEMA 17 motors is one of the most cost-effective upgrades you can make to a motion system. Start with one axis, dial in the tuning, and expand from there. If you hit a snag, reach out — we build these boards and we answer the phone.

