For my microcontroller course I built a pipeline that reads the orientation of an STM32 board and uses it to control a 6-axis stepper-driven robot arm.
The board I used was the STM32 B-L475E-IOT01A, which conveniently comes with the LSM6DSL and LIS3MDL onboard: a 6-axis IMU with a 3-axis gyroscope and accelerometer, and a 3-axis magnetometer.
The initial implementation read the raw values from the gyroscope and accelerometer and used them individually to calculate the board's orientation: gyroscope rates integrated into pitch, yaw and roll, with the accelerometer's gravity vector as a reference. This worked to an extent.
There were a few problems though:
The solution was to add the magnetometer and fuse all three sensors with the Madgwick algorithm, which works out how much to trust each sensor at any given moment and outputs a stable quaternion. That keeps yaw anchored and the estimate accurate during movement. The data is read over I2C inside CubeIDE, fused, then streamed over UART to a Python script on the laptop.
The Python side renders a live 3D visualization of the board's orientation and detects tilt commands. Once pitch or roll crosses a threshold, it classifies the motion as a forward, backward, left or right command and forwards it over serial to an Arduino UNO.
The Arduino drives the arm using six NEMA steppers paired with A4988 drivers, with a micro limit switch at each joint for homing. On startup every joint rotates until its switch triggers, the same principle 3D printers use, and the arm settles into its inverted-L home stance. Without that reference position, joints can overextend, and kinematic control later on becomes impossible. I also recorded a video tutorial of the full hardware build.
The full chain: IMU data over I2C → Madgwick filter → UART → Python tilt detection → serial → Arduino → stepper motors.
So far the demo controls joints 1-5 (excluding 4) directly from the board's orientation data. Tilting or rotating the board moves the corresponding joint.
What's missing is inverse kinematics. Right now joints move individually in response to tilt, which works but is limited. The actual goal is Cartesian-space control: give the arm a target position and let it solve for the joint angles. I've already characterized the Denavit-Hartenberg parameters, so the IK solver is the obvious next step. In the meantime the plan is to adapt Maximilian Beck's robot GUI, a JavaScript tool where you describe the robot's geometry and it solves the inverse kinematics to a target pose in real time.