Microcontroller Project: Gesture-Controlled Robot Arm

Timeline: Spring 2025 Context: ETH Project Based Learning Lab Supervision: Dr. Philipp Meyer Alejo Restrepo

For the end project of my microcontrollers course we were given an STM32 board and had to build something around it. I wanted it to be about robot arms, so the board became the controller of a 6-axis arm I designed and built, and its gyroscope and accelerometer steer the arm with the tilt of the board.

Tilt forward and the arm moves in positive X, backward is negative X, side to side is Y, up and down is Z. The inverse kinematics run in Python. The microcontroller was my excuse to do a mainly mechanical project.

The mechanics

The arm is built the way 3D printers are: every joint has its own stepper driving a belt loop onto a large printed pulley, and everything else is printed too.

The full 6-axis arm assembly in SolidWorks, in its home stance
The full assembly in SolidWorks, in the home stance.

The base and shoulder sit on thrust bearings around an aluminium rod. The shoulder motor has a 5:1 planetary reduction and its belt runs past two 608 bearings as idlers onto an 80-tooth pulley.

Section view of the base joint in SolidWorks: the aluminium rod on bearings inside the base, with the stepper mounted beside it
Section through the base: the aluminium rod on its bearings, the stepper beside it.
CAD view of the shoulder joint: geared NEMA 17, 16-tooth pulley, 608 idlers, 80-tooth pulley and limit switch
The shoulder: geared NEMA 17, 16-tooth pulley, 608 idlers routing the belt onto the 80-tooth pulley, limit switch.

The elbow motor sits lower down to cut the moment of inertia of the joint, with adjustable idlers to set the belt tension.

The forearm is the tricky part. A pancake stepper turns the wrist, and a small round stepper at the back drives a long thin rod through the forearm to the front, where the belt twists 90° over two chamfered idlers to move the last joint. The heavier motors stay near the elbow, so the arm carries little weight at the tip.

CAD front view of the elbow joint: 100-tooth pulley, two adjustable idlers, motor pulley below
The elbow: 100-tooth pulley on the joint, two idlers on adjustable mounts for belt tension.
Section through the forearm: the thin aluminium rod running the length of the forearm from the rear NEMA 11 to the wrist
Section through the forearm: the thin rod runs from the rear NEMA 11 through the whole forearm to the wrist.
Side view of the forearm and elbow in CAD, with the elbow stepper, belt and the wrist joint
Side view of the elbow and forearm.
CAD close-up of the wrist: the belt twisted 90° over two chamfered idlers onto the 32-tooth pulley, with a limit switch
The wrist: the belt twists 90° over two chamfered idlers onto the 32-tooth pulley of joint 5.

The electronics

Everything electrical lives in a separate box: an IEC inlet through an emergency stop into a 12 V, 20 A supply, and an Arduino on a RAMPS board with DRV8825 drivers, the same breakout 3D printers use. Every joint has a limit switch and homes into it at startup, again like a printer.

The side panel carries a USB-B pass-through, an RJ45 for the switch signals, a 5 V jack and one GX16 connector per motor.

The open control box: 12 V 20 A power supply, Arduino with RAMPS board and DRV8825 drivers, the base motor's driver, fan and terminal blocks
Inside the box: the 12 V supply, the RAMPS board with its drivers, the base driver, fan and terminals.
The side panel of the control box: USB-B, RJ45, a 5 V jack, ventilation slots and six GX16 motor connectors
The side panel: USB-B, RJ45 for the switch signals, 5 V jack, and one GX16 connector per motor.
Diagram of the arm's components: Arduino, stepper drivers, six NEMA steppers and limit switches
The Arduino drives six steppers through their drivers, with one limit switch per joint.
Limit-switch homing rationale beside the technical drawing of the arm's home stance
Each joint drives into its limit switch to find an absolute reference. The drawing shows the home stance and the joint ranges.

The workflow

The board is the STM32 B-L475E-IOT01A, with a gyroscope, an accelerometer and a magnetometer on it.

The LSM6DSL IMU and LIS3MDL magnetometer on the B-L475E-IOT01A board
The LSM6DSL 6-axis IMU and the LIS3MDL 3-axis magnetometer on the B-L475E-IOT01A.

Reading the gyroscope and accelerometer on their own worked to an extent: the integrated gyro drifts, the accelerometer is noisy in motion, and yaw has no reference.

The first pipeline: raw IMU readings mapped to pitch, yaw and roll in CubeIDE and sent to a Python 3D view
The first version mapped raw LSM6DSL readings to pitch, yaw and roll in CubeIDE and sent them to a Python 3D view.

The chain that works: IMU over I2C, a Madgwick filter that fuses the sensors into a stable orientation, then Python, which shows the tilt on screen and only passes on tilts past 15°, so nothing moves by accident. Python sends the commands over serial to the Arduino, which drives the steppers.

Sensor fusion pipeline: I2C sensor reads, Madgwick fusion in CubeIDE, orientation stream over UART
On the board: I2C sensor reads → Madgwick fusion in CubeIDE → orientation over UART.
Live 3D visualization of the board's orientation in Python, with tilt detection and the Arduino link status
The Python view of the board's tilt, with the threshold, smoothing and the Arduino link status.
End-to-end signal flow: STM32 IMU, Madgwick filter, Python, Arduino, stepper motors
The full chain: STM32 IMU → Madgwick filter → Python → Arduino → stepper motors.