STM32 for robotics: a beginner’s practical guide

Why and when to move from Arduino to STM32, board selection, CubeMX setup, 20 kHz PWM, hardware encoder counting, current sensing and the wiring mistakes that kill boards.

Reference
RE-1364
Published
Read
7 min

About the author · Sources, testing and AI policy

STM32 development board wired to a motor driver and encoder on a small robot chassis

An Arduino is a great first controller and a poor choice for a robot with four motors, two encoders and a current sensor. At that point the hardware timers, encoder interfaces and CAN bus of an STM32 stop being luxuries. STM32 for robotics makes sense once a robot has several motors, encoders and a current sensor to manage.

This guide covers choosing an STM32 board, setting up the toolchain, using timers for PWM and hardware quadrature encoders, reading current with the ADC, and talking to a companion computer over UART or micro-ROS. It ends with the wiring mistakes that destroy boards on the first power-up.

Why STM32 for robotics rather than an Arduino board

FeatureArduino UnoSTM32F103STM32F4 or G4
Core8-bit AVR, 16 MHz32-bit Cortex-M3, 72 MHzCortex-M4, 80 to 180 MHz
Hardware timers34 general purposeMany, with advanced features
Encoder interfaceSoftware interruptHardware quadrature on timersHardware quadrature, more channels
CANNoneOne controllerOne or two controllers
ADC10-bit12-bit12-bit, faster, multiple units
Floating pointSoftwareSoftwareHardware single precision
Board cost in IndiaLowLowModerate

The practical difference is deterministic timing. Hardware quadrature decoding counts encoder edges in silicon, so a busy loop cannot miss pulses. Advanced timers generate complementary PWM with dead time for a motor driver, which software cannot fake reliably.

Choosing a board

  • Blue Pill, F103. Cheap and widely available, good for a differential drive robot with two encoders. Watch for clone quality and missing USB bootloader support.
  • Nucleo boards. Official, with an onboard programmer and debugger, better documentation and fewer surprises. Slightly larger.
  • G431. Modern Cortex-M4 with a good analog front end, a sensible choice for motor control.
  • F411 and F401. More speed and memory for micro-ROS or sensor fusion.
  • H7 series. Only when you genuinely need the performance, because cost and complexity rise sharply.

For a first STM32 robot, a Nucleo with an onboard ST-Link removes an entire class of setup problems. Buy the clone boards once you know the workflow.

Toolchain: CubeIDE, HAL and the Arduino option

ST provides STM32CubeIDE and CubeMX for pin and clock configuration. The HAL library is verbose but portable, and it is the fastest route to working peripherals. The Arduino core for STM32 lets you keep the familiar API, at the cost of less control over timers.

  • Use CubeMX to set the clock tree, then generate code and keep your logic in the marked user sections.
  • Set the debug pins early, before you need them.
  • Prefer HAL for a first project; move to LL or direct register access only for timing-critical loops.
  • Keep the generated code out of version control conflicts by writing your own files and calling them.

PWM for motors

A motor driver needs a PWM signal with a frequency high enough to avoid audible whine and low enough to keep switching losses reasonable. Twenty kilohertz is a common compromise for small DC motors. STM32 for robotics uses hardware timers to generate motor PWM, which is where the board starts to beat an Arduino.

// TIM2 channel 1 at 20 kHz on a 72 MHz clock
// prescaler 0, period 3600 gives 72e6 / 3600 = 20 kHz
htim2.Init.Prescaler = 0;
htim2.Init.Period = 3600 - 1;
HAL_TIM_PWM_Init(&htim2);

TIM_OC_InitTypeDef oc = {0};
oc.OCMode = TIM_OCMODE_PWM1;
oc.Pulse = 0;
oc.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim2, &oc, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);

// duty 0 to period
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 1800);

Compute the period from the timer clock rather than copying a number. The timer clock on STM32 is not always the system clock, and a wrong assumption produces a motor whine at an unexpected pitch and a driver that runs hotter than expected.

Hardware quadrature encoders

Put the encoder on a timer in encoder mode. The hardware counts up and down as the two channels change, with no interrupt load and no missed edges at speed.

TIM_Encoder_InitTypeDef enc = {0};
enc.EncoderMode = TIM_ENCODERMODE_TI12;
enc.IC1Polarity = TIM_ICPOLARITY_RISING;
enc.IC2Polarity = TIM_ICPOLARITY_RISING;
enc.IC1Filter = 6;
enc.IC2Filter = 6;
HAL_TIM_Encoder_Init(&htim3, &enc);
HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);

int16_t delta = (int16_t)__HAL_TIM_GET_COUNTER(&htim3);
__HAL_TIM_SET_COUNTER(&htim3, 0);   // read and reset each control cycle

Read and reset in the same control cycle, then convert counts to distance using the encoder resolution and gear ratio. Filtering the input pins in hardware saves software debounce and prevents false counts from motor noise.

Current sensing with the ADC

Motor current tells you far more than a stall flag. After a simple moving average, it gives load estimation, jam detection and a crude torque limit. Use a dedicated current sensor IC rather than a shunt alone, and keep the analog ground separate from motor return currents.

HAL_ADC_Start(&hadc1);
if (HAL_ADC_PollForConversion(&hadc1, 2) == HAL_OK) {
  uint32_t raw = HAL_ADC_GetValue(&hadc1);
  float volts = raw * 3.3f / 4095.0f;
  float amps = (volts - 1.65f) / 0.066f;   // example sensitivity
}
HAL_ADC_Stop(&hadc1);

Calibrate the zero point with the motor disabled. A sensor with a 1.65 V zero and a small offset will report a steady current when there is none, and a jam detector built on that reading will trip constantly.

Talking to a companion computer

Send a compact binary frame over UART rather than text. A header byte, a length, a sequence number, the payload and a checksum is enough to recover from framing errors, which do happen when the cable runs beside motor wires.

  • Fix the baud rate on both sides and confirm it before debugging logic.
  • Sequence numbers expose dropped frames that checksums alone hide.
  • Send at a fixed rate from a timer, not from the main loop.
  • For ROS 2, micro-ROS removes most of the framing work and gives you topics and parameters.
  • Add a watchdog: if the companion stops sending, stop the motors.

Debugging and bring-up

ST-Link with SWD gives breakpoints and variable inspection over two wires. Learn to use it before the robot is assembled, because debugging a live robot with a USB cable attached is awkward and sometimes dangerous.

  • Print over UART at a low rate during bring-up; printing every loop breaks timing.
  • Blink an LED in the fault handler so a crash is visible without a debugger.
  • Enable the hardware watchdog once the robot leaves the bench.
  • Keep a map of which pin does what; label the motor connectors physically.

Mistakes that kill boards

MistakeResultPrevention
5 V signal into a 3.3 V pinDamaged pin or chipLevel shifter or open drain
Motor supply back EMFBrownout and resetsFlyback diodes, separate supplies, bulk capacitor
Floating input pinsErratic behaviourPull-up or pull-down configured
Boot0 held highBoard waits for a programmerCheck the boot jumper
Shared ground returnADC noise and encoder glitchesStar ground, separate power ground
No decoupling capacitorRandom resets under load100 nF at every supply pin

Power the STM32 from its own regulator or a clean 5 V source, and keep motor current out of the logic ground path. Most unexplained resets on a first robot are a grounding problem, not a firmware problem.

Related: ESP32 FreeRTOS tasks, Arduino versus ESP32, micro-ROS tutorial, Arduino interrupts and encoders, BLDC motors and ESCs, robot controller PCB design.

Is STM32 too hard for a beginner?

Not if you start with CubeMX and a Nucleo board. The barrier is the toolchain, not the chip, and the payoff is deterministic timing, hardware encoder decoding and a CAN controller that an Arduino cannot match.

What is the difference between HAL and LL?

HAL is a portable abstraction with more overhead; LL uses lightweight inline functions closer to the register. Start with HAL, and move a specific function to LL only if timing demands it.

Can I use the Arduino IDE with STM32?

Yes, through the official Arduino core for STM32. It is convenient, but you lose fine control over timers, which is often the reason to move to STM32 in the first place.

How do I count encoder pulses without missing any?

Configure a timer in encoder mode so the hardware counts the quadrature edges. Reading the counter in a loop in software will miss pulses as speed rises.

Do I need micro-ROS on the microcontroller?

Only if you want the STM32 to appear as a ROS 2 node. A simple binary UART protocol is lighter and easier to debug, and micro-ROS adds a broker and more memory use.

The STM32 peripherals that make CAN practical, including hardware filters, are covered in the CAN bus guide.

Sources and further reading

Pass it onWhatsAppLinkedIn

Keep reading

Continue learning

Tutorials

Construction Robotics India: What Works Now

Survey, layout, demolition, shotcrete and prefabrication robots in India, the honest state of 3D printing, costs and where automation actually pays.

  • Published28 Oct 2026
  • Read6 min

Free: the final-year project pack

A shortlist with BOMs, a report outline that matches the usual chapter scheme, a costing sheet and 40 viva questions. Built for the February–April submission season.

The Build Note

One short email when something is worth your bench time.

A new build sheet, a part price that moved, or a mistake worth avoiding. No daily drip, no recycled news.

One short email when a build or a price is worth your time. Unsubscribe in one click.