Skip to content

Beeps And Songs

Tim edited this page Nov 2, 2026 · 1 revision

Beeps & Songs (the Buzzer)

Your Ender-3 V3 KE has a small buzzer on the board. Stock firmware only ever used it for one fixed, loud beep. NebulaOS drives it with hardware PWM instead, so it can play any pitch, any length, and full melodies — and it does so with zero CPU load, which means it's safe even in the middle of a print.

There are three things this gives you:

  1. A soft touch click when you tap the screen (optional).
  2. M300 beeps with real pitch and duration (the standard slicer "beep" command).
  3. PLAY_TUNE songs — built-in tunes, plus your own, kept in an editable file.

The M300, BEEP and PLAY_TUNE macros are part of the NebulaOS Klipper config (/etc/nebulaos/klipper/beeper.cfg). Klipper itself owns the buzzer: the nebulaos_beeper extra drives the kernel's PWM channel for it (/sys/class/pwm/pwmchip0/pwm3) and plays every sound in the background, so a beep or a song never pauses the print. The screen's touch click goes through the same owner. /opt/nebulascreen/nebulabeep still exists for old scripts, but it only forwards to Klipper now; the old RUN_SHELL_COMMAND CMD=guppybeep / CMD=nebulabeep names are gone - call M300, PLAY_TUNE or NEBULAOS_BEEP FREQ=<Hz> DURATION=<ms> instead. NEBULAOS_BEEPER_STOP silences the buzzer.


1. Touch click (screen feedback)

A quiet, low "tick" on every button tap — like a phone's touchscreen feedback.

  • Turn it on/off: Settings → Display → Touch beep. (Default is off.)
  • It only clicks on real taps, not when you scroll or drag.
  • It's deliberately soft and low — not the old loud beep. Nothing to configure; flip it on and it just works.

2. M300 — a single beep

The standard slicer/G-code beep command, now with real pitch:

M300 S<frequency Hz> P<duration ms>
Example What you hear
M300 S1000 P200 1 kHz tone for 200 ms
M300 S440 P500 low A note, half a second
M300 S2000 P80 short high blip
BEEP quick confirmation chirp (shortcut for M300 S2000 P120)

Many slicers can emit M300 on events (e.g. "beep when the print finishes") — now it plays the pitch and length you actually ask for.


3. PLAY_TUNE — play a song

PLAY_TUNE SONG=mario
PLAY_TUNE SONG=zelda
PLAY_TUNE SONG=starwars
PLAY_TUNE SONG=success      # short happy jingle
PLAY_TUNE SONG=error        # short low alert

These names come from a plain text file you can edit (see below). Great for a "print complete" or "print failed" jingle.

Add your own songs (songs.conf)

Songs live in:

/opt/printer_data/config/songs.conf

That's in the same config folder you already see in Mainsail/Fluidd's file manager, so you can edit it right in your browser (or over SSH). The format is one song per line, name = RTTTL:

# Lines starting with # or ; are comments.
mario   = mario:d=4,o=5,b=100:16e6,16e6,32p,16e6,...
success = success:d=4,o=6,b=140:8c,8e,8g,c
tetris  = tetris:d=4,o=5,b=160:e6,8b,8c6,8d6,16e6,16d6,8c6,8b,a,8a,8c6,e6,8d6,8c6,b

To add a tune: grab an RTTTL string (the old Nokia ringtone format — thousands are online), paste it in, give it a name, save, then run FIRMWARE_RESTART. Now PLAY_TUNE SONG=yourname plays it. No recompiling, no reflashing.

Two things worth knowing:

  • Your songs survive updates. NebulaOS only creates songs.conf if it's missing — a NebulaOS update never overwrites the one you've edited.
  • Use normal # sharps here. In songs.conf you can paste RTTTL exactly as found online (a#, f#, …). The buzzer player reads this file directly, so the sharps come through fine.

Playing a one-off without editing the file

PLAY_TUNE RTTTL=mytune:d=4,o=5,b=120:c,e,g,8c6

For this inline form only, there are two rules (because the command passes through Klipper's G-code parser first):

  • No spaces in the string.
  • Write sharps as s (e.g. as, cs, fs) — not #, which G-code treats as a comment.

For anything you'll reuse, just put it in songs.conf and use SONG= — no rules to remember.


Using it from your slicer

Drop a command in your slicer's End G-code, on its own line:

PLAY_TUNE SONG=mario

It's a normal Klipper command, so the slicer passes it straight through. A few tips:

  • Put it at the very end of your end-gcode. The song starts when the moves before it have finished and plays in the background - the printer does not wait for it.
  • For a quick "done" chirp instead of a whole song, use M300 S1500 P300 or BEEP.
  • The slicer's end-gcode only runs on a successful finish. To get a sound on failure, trigger PLAY_TUNE SONG=error from a cancel/error macro instead.

Songs mid-print are cut short. Nothing waits for a song, but while a print runs a tune is cut to 20 seconds so a long song can't buzz through half the job. Save long songs for idle moments — print done, error, startup.


Good to know

  • Some notes are louder than others. The buzzer is a tiny piezo with a natural resonance around 2.3 kHz, so volume varies a bit from note to note. The pitch is always accurate — only loudness changes. That's the physical hardware, not a bug.
  • It's a 1-bit beeper — think classic 8-bit chiptune, not hi-fi. There's no speaker or audio chip on the KE.
  • Safe during prints. The tone itself is hardware PWM (no CPU); Klipper only switches notes, and it never holds the G-code queue to do so.

Clone this wiki locally