Time Estimate: 3-4 hours (Build: 1 hr, Tuning: 2-3 hrs)
Target Board: ESP32 DevKit V1 (WROOM-32, 30-pin variant)
Suspending a steel object in mid-air using an electromagnet isn't magic; it is a high-speed control loop balancing gravity against electromagnetic force. For a successful project magnetic levitation build, you need a microcontroller capable of fast analog-to-digital conversion and high-frequency PWM, a linear Hall effect sensor to measure distance, and a logic-level MOSFET to drive the inductive coil. This guide walks you through the electromagnetic theory, exact hardware selection, and the PID control code required to achieve stable levitation.
The Physics and Theory Behind Magnetic Levitation
To control levitation, you must understand the non-linear relationship between distance and magnetic force. The attractive force $F$ exerted by an electromagnet on a ferromagnetic object is roughly proportional to the square of the magnetic flux density $B$ and the pole face area $A$:
$F = \frac{B^2 A}{2 \mu_0}$
Where $\mu_0$ is the permeability of free space. However, the magnetic field strength $B$ at a distance $r$ from the coil drops off non-linearly (approximately $1/r^3$ in the far-field). This means if the object drops just a few millimeters, the attractive force plummets. Conversely, if it gets too close, the force spikes exponentially, snapping the object to the core.
To stabilize this, we use a PID (Proportional-Integral-Derivative) controller. The Hall sensor provides a continuous voltage proportional to the magnetic field (which correlates to distance). The ESP32 reads this voltage, calculates the error from our target setpoint, and adjusts the PWM duty cycle to the MOSFET. The Proportional term reacts to current error, the Integral term eliminates steady-state offset, and the Derivative term dampens oscillation caused by the $1/r^3$ force curve.
Hardware Spec Sheet and Pin Mapping
Do not use an L298N motor driver for this. It has a 2V voltage drop and switches too slowly for the high-frequency PWM required to prevent audible whining and control lag. Use a logic-level N-channel MOSFET instead.
| Component | Exact Variant / Spec | Purpose |
|---|---|---|
| Microcontroller | ESP32 DevKit V1 (WROOM-32, 30-pin) | Fast ADC and high-res PWM generation |
| Hall Sensor | TI DRV5053A1QLPG (Analog) | Outputs 0.1V to 1.9V linear to magnetic field |
| MOSFET | IRFZ44N (Logic-Level, N-Channel) | Drives electromagnet; Vgs(th) < 2V for 3.3V logic |
| Electromagnet | 12V DC Lifting Magnet (25kg force) | Provides the lifting force (approx. 0.4A draw) |
| Diode | 1N4007 (or 1N5819 Schottky) | Flyback diode to clamp inductive kickback |
| Power Supply | 12V 2A DC Switching Supply | Powers the electromagnet |
Pin Mapping Table
| ESP32 Pin | Component | Notes |
|---|---|---|
| 3V3 | DRV5053 VCC | Sensor operates on 3.3V |
| GND | DRV5053 GND, MOSFET Source, 12V PSU GND | Common ground is critical |
| GPIO 34 (ADC1_CH6) | DRV5053 OUT | Input only pin; ideal for ADC |
| GPIO 25 | MOSFET Gate (via 100Ω resistor) | PWM output to drive coil |
Step-by-Step Build and Wiring Procedure
- Mount the Sensor: Glue the DRV5053 sensor to the bottom face (the flat pole) of the electromagnet. The flat side of the sensor package should face downward toward the levitating object. The plastic casing of the sensor will act as a physical spacer.
- Wire the Flyback Diode: Connect the 1N4007 diode in parallel with the electromagnet coil. The cathode (striped end) must connect to the 12V positive wire, and the anode connects to the MOSFET drain. This provides a safe loop for the inductive spike to dissipate.
- Wire the MOSFET Gate: Connect ESP32 GPIO 25 to the MOSFET gate through a 100Ω resistor (limits gate charging current). Add a 10kΩ pulldown resistor between the gate and ground to ensure the MOSFET stays off while the ESP32 boots.
- Establish Common Ground: Connect the 12V power supply ground, the MOSFET source, and the ESP32 GND pin together. Star grounding (all grounds meeting at one physical point) is highly recommended to keep 12V switching noise out of the ESP32's 3.3V ADC reference.
- Power Up: Plug in the 12V supply first, then connect the ESP32 to your PC via USB. Keep your fingers clear of the electromagnet face during initial testing.
ESP32 PID Control Code (Complete & Compilable)
This code targets the ESP32 DevKit V1 (WROOM-32) using the ESP32 Arduino Core 3.x. It implements a custom PID loop to avoid external library dependencies and includes error handling for sensor disconnects and ADC saturation. The ESP32 ADC is notoriously non-linear above 2.5V, but the DRV5053 maxes out at 1.9V, keeping us in the accurate linear region.
// Target Board: ESP32 DevKit V1 (WROOM-32)
// Core: ESP32 Arduino Core 3.x
#include
#define HALL_PIN 34
#define PWM_PIN 25
#define PWM_FREQ 20000 // 20kHz to avoid audible coil whine
#define PWM_RES 10 // 10-bit resolution (0-1023)
// PID Tuning Parameters (Requires manual tuning for your specific magnet)
float Kp = 25.0;
float Ki = 0.8;
float Kd = 12.0;
float setpoint = 1400; // Target ADC value (approx 1.1V, mid-range of sensor)
float integral = 0;
float lastError = 0;
unsigned long lastTime = 0;
int errorCount = 0;
void setup() {
Serial.begin(115200);
pinMode(HALL_PIN, INPUT);
// Initialize LEDC PWM for ESP32 Core 3.x
ledcAttach(PWM_PIN, PWM_FREQ, PWM_RES);
ledcWrite(PWM_PIN, 0); // Ensure magnet is OFF at boot
Serial.println("Magnetic Levitation System Initialized.");
lastTime = micros();
}
void loop() {
unsigned long now = micros();
float dt = (now - lastTime) / 1000000.0; // Delta time in seconds
lastTime = now;
// Read Sensor
int rawADC = analogRead(HALL_PIN);
// Error Handling: Check for disconnected sensor or ADC saturation
if (rawADC <= 10 || rawADC >= 4085) {
errorCount++;
if (errorCount > 50) {
ledcWrite(PWM_PIN, 0); // Kill power immediately
Serial.println("ERR: HALL_SENSOR_DISCONNECT");
delay(1000); // Prevent serial flood
return;
}
} else {
errorCount = 0;
}
// PID Calculation
float error = setpoint - rawADC;
integral += error * dt;
// Anti-windup: clamp integral
if (integral > 500) integral = 500;
if (integral < -500) integral = -500;
float derivative = (error - lastError) / dt;
float output = (Kp * error) + (Ki * integral) + (Kd * derivative);
lastError = error;
// Constrain and apply PWM
int pwmOut = constrain((int)output, 0, 1023);
ledcWrite(PWM_PIN, pwmOut);
// Telemetry (Throttled to not block loop)
static unsigned long lastPrint = 0;
if (millis() - lastPrint > 100) {
Serial.printf("ADC: %d | PWM: %d\n", rawADC, pwmOut);
lastPrint = millis();
}
// Maintain tight loop timing (approx 1kHz control loop)
while(micros() - now < 1000);
}
Debugging: First Three Things to Check When It Fails
When tuning a magnetic levitation project, failure usually manifests as violent oscillation, immediate dropping, or microcontroller resets. Here is the ranked troubleshooting path.
1. ESP32 Resets with 'Brownout detector was triggered'
Exact Error String: Brownout detector was triggered (Printed to serial monitor upon crash).
- Cause A (Most Likely): Missing or reversed flyback diode. The inductive kickback from the electromagnet is collapsing the 3.3V regulator on the DevKit board.
- Cause B: Power supply sag. A 12V supply rated for only 1A will brown out when the electromagnet draws peak current. Ensure you are using a 12V 2A+ supply.
- Fix: Verify diode orientation (stripe to 12V). Add a 100µF electrolytic capacitor across the 12V and GND rails near the MOSFET.
2. Serial Prints 'ERR: HALL_SENSOR_DISCONNECT'
Exact Error String: ERR: HALL_SENSOR_DISCONNECT
- Cause A: The sensor is wired to a pin that doesn't support ADC, or the 3.3V wire has broken loose.
- Cause B: The object is physically pressed hard against the sensor, saturating the DRV5053 output beyond the 4085 threshold.
- Fix: Verify wiring to GPIO 34. Ensure the object is removed from the magnet face before powering on so the PID loop can establish a baseline.
3. Object Oscillates Violently and Drops
- Cause: The Derivative (Kd) term is too low to dampen the $1/r^3$ force curve, or the PWM frequency is too low, causing step-wise force application.
- Fix: Increase
Kdin increments of 2.0. EnsurePWM_FREQis at least 15000Hz (20kHz is ideal). If it still oscillates, your control loopdtis too slow; remove anydelay()functions from the loop.
Extending and Simplifying the Build
How to Simplify: If tuning a custom PID loop sounds tedious, you can bypass the microcontroller entirely by purchasing a pre-built analog levitation module like the ZKY-01 Magnetic Levitation Kit. These use dedicated analog op-amp circuits (like the LM358) to handle the PID loop in hardware. It limits your ability to log data or customize the physics, but guarantees a working build in 10 minutes.
How to Extend:
- Add Visual Telemetry: Wire an I2C OLED display (SSD1306) to GPIO 21 (SDA) and 22 (SCL) to graph the real-time PID error and PWM duty cycle. This makes tuning vastly easier than reading serial prints.
- Reactive Lighting: Add a WS2812B RGB LED ring around the electromagnet core. Map the LED color to the raw ADC value: blue when the object is too far, green when perfectly levitating, and red when it drops.
FAQ: Project Magnetic Levitation
How much weight can a DIY project magnetic levitation support?
For a standard DIY build using a 12V 25kg lifting electromagnet, the practical dynamic levitation limit is usually between 150g and 300g. While the magnet can hold 25kg when flush against steel, the $1/r^3$ drop-off in magnetic field strength means that at a 10mm air gap (required for the sensor and stable control loop), the available lifting force drops to less than 5% of its rated flush capacity. To levitate heavier objects, you must use a high-voltage (24V+) custom-wound coil and a high-current MOSFET.
Why does my project magnetic levitation oscillate and drop the object?
Oscillation is the hallmark of an untuned PID derivative term or a control loop that is executing too slowly. Because magnetic force increases exponentially as the object gets closer, a standard Proportional controller will over-correct, pushing the object away, then under-correct, letting it fall. The Derivative term acts as a 'shock absorber,' reducing power before the object reaches the setpoint. If your microcontroller loop takes longer than 2 milliseconds to execute (often caused by blocking Wi-Fi functions or excessive serial printing), the system will inherently oscillate.
Can I use an Arduino Uno instead of an ESP32 for this project magnetic levitation?
You can, but it is not recommended for high-performance levitation. The Arduino Uno's ATmega328P has a 10-bit ADC that maxes out at roughly 9.6kHz sampling rate, and its default PWM frequency is 490Hz (which will cause loud audible whining from the electromagnet coil). The ESP32's 12-bit ADC and ability to generate 20kHz+ PWM on dedicated hardware timers provide the tight, high-speed control loop necessary to stabilize the non-linear magnetic field without audible noise.






