← All projects

3-Wheel Robot

  • STM32F401RBT6
  • MPU6050
  • KiCad
  • ESP8266
  • WebSockets
The assembled robot from above: acrylic chassis, control board, two geared motors with yellow wheels
The assembled robot. The control board is on the upper deck, with the motors and wheels below and the ESP8266 next to the board.
MCU
STM32F401RBT6 (ARM Cortex-M4)
IMU
MPU6050 3-axis gyro and 3-axis accelerometer, over I²C
Toolchain
STM32CubeIDE, HAL drivers, C
Control
ESP8266 access point serving its own web page, driven from a phone browser over WebSockets
Board
Custom control PCB designed in KiCad

What it is

A three-wheel robot running on a control board I designed, laid out, and hand-soldered. You connect a phone to the robot's own WiFi network, open its address in a browser, and get a control pad for driving forward, backward and turning. Roll and pitch from the IMU show up live on the same page.

I designed the schematic and layout and assembled the board. The STM32 firmware was provided by my professor; I built it and flashed the board with STM32CubeIDE and STM32CubeProgrammer. The board went through several revisions, and each one is in the repository, from the first two-wheel board to the current one.

How it's put together

Module What it does
firmware_main The first board, a two-wheel version: KiCad files, gerbers, an interactive BOM, a CAD model of the assembly, and build photos. No firmware, despite the folder name.
firmware_v2 KiCad project for a later board revision
balance_noEncoder The STM32 firmware: MPU6050 through a complementary filter, TB6612 motor control, and the UART link to the ESP8266
remote_esp8266 ESP8266 access point, web server, and the WebSocket control page for the phone
pcb KiCad project for the control board: schematic and layout

Driving it from a phone

There's no pairing and no app. The ESP8266 runs its own access point, so the robot is its own WiFi network. You join it, open the ESP's address, and it serves the control page itself. Any phone with a browser works, and no internet connection is needed.

Commands go over a WebSocket instead of separate HTTP requests. With one request per button press, every command waits on a round trip and the robot lags behind your input. An open socket sends small JSON messages both ways continuously. The ESP8266 passes each command to the STM32 over UART. The data coming back is also useful for debugging, since I can watch roll and pitch while the robot is moving instead of reading them over a serial cable afterward.

The board

The control board puts everything the robot needs on one PCB, built around an STM32F401RBT6 on a 16 MHz crystal. A TB6612FNG drives the motors. The MPU6050, the MT6701 magnetic encoders, the ESP8266 and the motor leads each plug into their own connector instead of being soldered to the chassis. Power comes in over USB-C through a PTC fuse, CC resistors and a USBLC6 ESD array, then LD1117 regulators make 5 V and 3.3 V rails, each with an indicator LED. A WS2812B gives the firmware one addressable status light, and a 4-pin SWD header keeps the debugger connected.

The switch labeled 5 V selector cuts power to the motors while the logic stays powered from USB. That way I can flash and debug the board on the bench without the robot driving itself off the desk.

Populated by hand

The boards came back bare, and I soldered every surface-mount part by hand: the passives, both regulators, the TB6612, the crystal, and the STM32 in a 64-pin quad flat pack.

I planned the layout around that. Parts are spaced far enough apart for an iron and tweezers instead of packed as tightly as the design rules allow. Anything that takes mechanical stress, like the USB-C port, the JST connectors and the headers, is through-hole, because surface-mount pads can peel off.

The bare control board before assembly, showing empty pads and silkscreen reference designators
Bare board, as it came from the fab.
The same board fully populated by hand: STM32 in a 64-pin QFP, regulators, motor driver, USB-C, and JST connectors
The same board populated. The QFP, SOIC packages and passives are all hand-soldered.
KiCad 3D render of the control board with the STM32 package, ESP8266 module, USB-C connector, MPU6050 and JST connectors
The v1 control board, rendered from KiCad.
Underside of the robot showing the motor mounts and wiring
Underside, with the motors and wiring.
KiCad schematic of the control board grouped into microcontroller, motor driver, regulators, IMU, encoder, ESP8266, and SWD sections
Schematic, grouped by function: MCU, motor driver, regulators, and a block per connector.
KiCad board layout of the control board
Layout. Each revision has its own board files in the repository.

Self-balancing (unfinished)

The original goal was a self-balancing robot, an inverted pendulum that stays upright by driving its wheels in the direction it's falling. That's why it has the IMU and encoder inputs. The firmware reads the MPU6050 through a complementary filter at 100 Hz to get roll and pitch, which is the first thing a balance controller needs.

I didn't get as far as a working balance controller, and the encoder code is still switched off, so for now it only drives. The board has what balancing would need: an IMU, encoder inputs and live telemetry.

Building it yourself

The firmware in balance_noEncoder/ is an STM32CubeIDE project. Open it with File → Open Projects from File System and build. The board files were saved in KiCad 9 and 10, so open them in KiCad 10.