Try it yourself: Für Elise on your own ESP and motor
Hi! The long log explains how and why a DC motor can play Beethoven - the physics, the maths, the heat. This page is just the doing. Wire three things, click a button in this browser tab, type two lines. About five minutes. ♥️
What you need
- An ESP32 board - any of them. The original ESP32, S2, S3, C2, C3, C5, C6, C61, H2 or P4. The firmware leans on the LEDC peripheral, which every ESP32-family chip has, so there is a build for each one.
- An H-bridge motor driver - DRV8833, TB6612FNG, L9110S, L298N. Anything with two input pins per motor.
- A small brushed DC motor - N20, TT gearmotor, an old toy motor, a fan motor. It does not need to be a good one.
- Chrome or Edge, on a desktop. The flashing happens over Web Serial, which Firefox and Safari do not have, and phones cannot do at all.
Plus a motor supply. A USB power bank, a 5 V wall adapter, 4×AA - whatever your motor is happy with. Do not power the motor from the ESP’s 3V3 pin.
Step 1 - Wire it
Three wires from the ESP to the driver, two from the driver to the motor, and the motor’s own power into the driver:
GPIO 4 and 5 are only the defaults - you can change them after flashing, so use whatever pins your board has free. Avoid the strapping pins (GPIO 0, 45, 46 on an S3) and anything wired to onboard flash or PSRAM.
Step 2 - Flash it
Plug the board in, then click the button that matches it.
The quick rule, which is right nearly every time:
- Original ESP32, S2 or C2 → variant A, always. These chips have no native USB at all, so there is nothing else it could be.
- Anything newer (S3, C3, C5, C6, C61, H2, P4) → try variant B first. Most modern devkits run their USB socket straight into the chip. Only fall back to A if your board has a separate bridge chip, or you plugged into the socket labelled UART on a two-socket board.
Or pick by looking at the board:
A port-picker appears - choose your board and let it run. It takes a minute or so and it detects which chip you have on its own.
If you are not sure which variant, just pick one. Flashing works either way; only the console differs. If you end up with a serial port that connects but never prints anything, that is the symptom of the wrong variant - come back and click the other button. Nothing is harmed by flashing twice.
Rather not flash from a browser? (manual download)
Every image is a single file that flashes at offset 0:
esptool.py --chip auto --port COM5 write_flash 0x0 fur-elise-esp32s3-usb.bin
Files are at /files/firmware/fur-elise/, named fur-elise-<chip>-<variant>.bin.
<chip> is one of esp32, esp32s2, esp32s3, esp32c2, esp32c3, esp32c5, esp32c6, esp32c61, esp32h2, esp32p4.
<variant> is uart or usb. Only chips with a native USB-Serial-JTAG peripheral have a usb build - the original ESP32, the S2 and the C2 are UART only, because they have no such peripheral to put a console on.
If you have one of the newer chips (C5, C61, H2, P4) the browser flasher may not recognise it yet - the buttons above depend on what ESP Web Tools supports, which trails the silicon. This manual route always works.
Step 3 - Open the console
When flashing finishes, click Logs & Console in the same dialog. (Or use any serial terminal at 115200 baud - PuTTY, screen, the Arduino IDE’s serial monitor, whatever you have.)
Press Enter and you should get a prompt:
furelise>
If you see the prompt, everything worked.
Two symptoms both mean “wrong variant” - go back to step 2 and click the other button:
- Nothing appears at all, or
- the banner appears but typing does nothing. On some boards the firmware can still print to the port you are on while listening for input on the other one, so text is not proof you picked right. A prompt that ignores you is.
Step 4 - Tell it your pins, then play
set in1 4
set in2 5
save
play
That is the whole thing. save writes the pins to flash so the board remembers them next time. Use stop to stop it.
If you wired to different GPIOs, put those numbers in instead. show prints everything the firmware knows.
Before you turn it up
The motor is stalled the entire time it plays. It is not spinning, so there is no back-EMF limiting the current and no rotation moving air over the winding. The amplitude setting is a current limit before it is a volume control, and the heat grows as its square - doubling it quadruples the dissipation.
It ships at a deliberately quiet 96 out of 255. Raise it in steps if you want more:
set amplitude 140
The test that matters: touch the motor after a couple of playthroughs. Warm is fine. If you cannot hold a finger on it for five seconds, turn it back down. The firmware also stops for a one-minute rest after two minutes of playing, which you can disable but really should not.
The full reasoning, with the numbers, is part 9 of the long log.
Making it louder - the free way first
Clamp the motor to something large and thin. A biscuit tin, a project box lid, a sheet of cardboard, the underside of your desk. A bare motor is a voice coil with no cone, and giving it one is by far the biggest improvement available - bigger than any setting.
Then hunt the resonance:
sweep
It walks a chromatic scale from 100 Hz to 1600 Hz and prints each frequency. Listen for where it suddenly leaps out, then move the piece there:
set transpose 12
Small motors are usually deaf below about 200 Hz, so transpose 12 (up one octave) helps almost every setup. Do the sweep after mounting - the mount is half of what sets the resonances.
Want it to spin while it plays?
set spin 100
The shaft turns and the tune keeps going. Sign picks the direction, range is −255 to 255. Around 100 gets you about 39% of full speed while keeping 82% of the volume - the maths for why that trade is so generous is in part 8 of the long log.
If something goes wrong
| What you see | Why | Fix |
|---|---|---|
| No port in the browser dialog | Charge-only cable, or missing driver | Use a data cable. For CP2102/CH340 boards, install the bridge driver |
| Port connects, nothing prints | Wrong console variant | Flash the other button in step 2 |
| Text prints but typing does nothing | Wrong console variant - it can print to one port while listening on the other | Flash the other button in step 2 |
| Flashing fails partway | Board not in bootloader | Hold BOOT, tap RESET, release BOOT, retry |
| Prompt works, no sound at all | Pins, sleep pin, or motor supply | Check show, check nSLEEP is high, check VM has power |
| Motor spins instead of buzzing | spin is not zero |
set spin 0 |
| Very faint | Not mounted, or too quiet | Clamp it to something big; raise amplitude in steps |
| Board resets when it plays | Motor supply browning out the ESP | Separate supply for VM, grounds still common |
| Sound is there but wrong pitch | Nothing - that is your motor | Try set transpose 12, and run sweep |
Commands
| Command | Does |
|---|---|
play / stop |
Start and stop the piece |
show |
Print state and every setting |
set <name> <value> |
Change one setting |
save |
Keep the current settings across reboots |
defaults |
Back to factory settings |
sweep |
Chromatic sweep for finding resonances |
help |
List everything |
| Setting | Default | Does |
|---|---|---|
in1 / in2 |
4 / 5 | H-bridge input GPIOs |
amplitude |
96 | Loudness and heat. Read the warning above |
spin |
0 | Rotate while playing, −255…255 |
spinmode |
antiphase | antiphase louder, unipolar cooler |
transpose |
0 | Semitones; 12 is up an octave |
tempo |
140 | Milliseconds per sixteenth note |
gap |
18 | Silence between notes |
pause |
2500 | Silence between repeats |
once |
0 | 1 plays through once and stops |
guardplay / guardrest |
120 / 60 | The forced cooling rest, in seconds |
And then read why it works
None of the above explains anything - it just gets a motor playing Beethoven. If you want to know why a motor can make a musical note at all, why the obvious way to do it makes the motor drive off with the tune, and how you get it to spin and sing at the same time:
→ Für Elise on a DC motor: the N20 learns to sing
That one has the physics, the Fourier maths, and an honest accounting of what this does to your motor.
If you get it playing, I would genuinely love to hear it. Send me a video on instagram. ♥️