MiniBeeps: Your Mini Sound Machine

by makerkid in Circuits > Microcontrollers

2405 Views, 26 Favorites, 0 Comments

MiniBeeps: Your Mini Sound Machine

MiniBeeps pose pic.JPG
F33LBQAMHMCL44S.jpg

Have you ever had those times where you just want to hit some buttons to play some beeps and notes, well then MiniBeeps is your best choice! it is a 3 by 3 button pad with a piezo buzzer for notes and beeps it also has a loop function to reapet cool notes!

Supplies

all parts 2.JPG

Tools:


soldering iron

hot glue gun

3D printer

screwdriver


Materials:


Seeed studio ESP32-C3 (1) Digikey

14mm Piezo Buzzer (1) Digikey

220 OHM resistor 1% (1) Digikey

Adafruit soft 8 by 8mm buttons 10 pack (1) Adafruit

male to male wires (11) Amazon

data USB-C cable (1) Amazon

M3 screws (1) Amazon

costum PCBs JLCPCB

Ordering the PCB From JLCPCB

Screenshot 2025-10-29 154554.png
Screenshot 2025-10-29 151539.png
Screenshot 2025-10-29 151740.png

time to order the PCB's! I recommend JLCPCB becuase of there high quality boards and fast fabrication and shipping. to start go to JLCPCB's website and make an account. then download the gerber files at the bottom.

now select Add gerber file and select MiniBeeps.gbr

finely select your board setttings (I left board settings as is) and order!

board fabrication and shipment takes about 5 - 6 days


(sorry if the schematic and board is a little messy and wonky its my first PCB that i have made:)


here is the schematic files:

for the Gerber files please click on this link to go to a google drive page to download:

MiniBeeps Gerbers

Downloads

3D Printing the Case

Screenshot 2025-11-09 131404.png
Screenshot 2025-11-09 131417.png

i have made a case for MiniBeeps in Tinkercad here are the download files:

the top and bottom go together with a press fit so less screws!

Soldering All Parts to the PCB

wires on pcb.jpeg
resistor on PCB.jpeg
buzzer on PCB.jpg

to start soldering the PCB, first solder the 220 ohm resistor and piezo buzzer. to solder the piezo buzzer make sure that the buzzers legs are in the right spots. the positive leg is the leg where on the body of the buzzer it has a little inward dot on the pin that is positive.


then solder nine buttons into there spots


next grab 11 wires and solder to the PCBs 11 pins

Soldering PCB Pins to the XIAO ESP32-c3

wires on XIAO.JPG

time to solder the PCB pins to the XIAO! grab 11 wires here are the connections:


Wires to XIAO:

PCB B1 - XIAO D0

PCB B2 - XIAO D1

PCB B3 - XIAO D2

PCB B4 - XIAO D3

PCB B5 - XIAO D4

PCB B6 - XIAO D5

PCB B7 - XIAO D6

PCB B8 - XIAO D7

PCB B9 - XIAO D8

PCB BUZZER INPUT - XIAO D9

PCB GND - XIAO GND


make sure to solder all pins to the top of the XIAO and bend them down so it fits in the case

Hot Gluing XIAO ESP32-C3 to the Case

XIAO gluing.JPG

grab your case bottom and glue in your XIAO into its spot and make sure that all the wires are sitting nicely

Screwing PCB Into Case and Putting Everything Together

buttons in top case.JPG
button leg cut.JPG
PCB screw in .JPG
MiniBeeps finished.JPG

to start cut off the four legs of the buttons that are on the piezo buzzer chamber. then grab 4 M3 screws and your case bottom, now screw in the PCB onto the the screw pillars. make sure that the piezo buzzer is fitting nicely into its hole as you screw in the PCB. after you have done all that take the case top and button pads and put the button pads into the top case.

then take both top and bottom cases and press them together watch if anything gets stuck or is getting squished so you can move it.

then flip over and your done the assembly!

Uploading the Code

Screenshot 2025-10-30 082033.png
Screenshot 2025-10-30 082127.png
Screenshot 2025-11-08 075208.png

to upload the code first download the latest version of Arduino IDE


Board manager:

now go to boards manager (on the left sidebar) and search for esp32 and install the one by Espressif Systems

then go to Tools, Board:, and select esp32 then scroll all the way down until you see XIAO_ESP32C3 and select it as your board.


Uploading the code:

make sure your XIAO ESP32-C3 is plugged in with a USB-C cable

now copy and and paste this code and then press upload!

#include <Arduino.h>

// ----- Pin assignments -----
const int PIN_B = D0;
const int PIN_C = D1;
const int PIN_D = D2;
const int PIN_E = D3;
const int PIN_F = D4;
const int PIN_G = D5;
const int PIN_A = D6;

const int PIN_LOOP = D7;
const int PIN_OCTAVE = D8;
const int PIN_BUZZ = D9;

const int NOTE_PINS[7] = {
PIN_B, PIN_C, PIN_D, PIN_E, PIN_F, PIN_G, PIN_A
};

// Base frequencies (Hz) for B, C, D, E, F, G, A
// B4, C5, D5, E5, F5, G5, A5
const int BASE_FREQ[7] = { 494, 523, 587, 659, 698, 784, 880 };

// ----- Octave handling -----
uint8_t currentOctave = 0; // 0 = base*1, 1 = *2, 2 = *4, 3 = *8
const uint8_t MAX_OCTAVE = 3;

int freqFor(int noteIndex) {
int f = BASE_FREQ[noteIndex];
f = f << currentOctave; // multiply by 2^octave
return f;
}

// ----- Button state -----
bool notePrevPressed[7] = {false};
bool noteHeld[7] = {false};

bool loopPrevPressed = false;
bool octPrevPressed = false;

// ----- Looper data -----
enum Mode { MODE_IDLE, MODE_RECORDING, MODE_PLAYING };
Mode mode = MODE_IDLE;

struct Event {
uint32_t t; // time (ms) from start of recording / loop
uint8_t note; // 0..6
bool on; // true = press, false = release
};

const uint16_t MAX_EVENTS = 512;
Event events[MAX_EVENTS];
uint16_t eventCount = 0;

uint32_t recStartMs = 0;
uint32_t loopLength = 0;
uint32_t playStartMs = 0;

// ----- Audio state -----
int currentNote = -1; // which note index is sounding, -1 = none

void startToneFor(int noteIndex) {
if (noteIndex < 0 || noteIndex > 6) return;
int f = freqFor(noteIndex);
tone(PIN_BUZZ, f);
currentNote = noteIndex;
}

void stopTone() {
noTone(PIN_BUZZ);
currentNote = -1;
}

// Choose which note to play from a held[] array: highest index wins
int pickHeldNote(const bool held[7]) {
for (int i = 6; i >= 0; --i) {
if (held[i]) return i;
}
return -1;
}

// ----- Events: keep them sorted by time -----
void addEvent(uint32_t t, uint8_t note, bool isOn) {
if (eventCount >= MAX_EVENTS) return;
int i = (int)eventCount - 1;
while (i >= 0 && events[i].t > t) {
events[i + 1] = events[i];
i--;
}
events[i + 1].t = t;
events[i + 1].note = note;
events[i + 1].on = isOn;
eventCount++;
}

void startRecording() {
mode = MODE_RECORDING;
eventCount = 0;
recStartMs = millis();

for (int i = 0; i < 7; ++i) {
noteHeld[i] = false;
notePrevPressed[i] = false;
}

stopTone();
}

void startPlayback() {
// Close any notes still held at end of recording
uint32_t endTime = millis() - recStartMs;
for (int i = 0; i < 7; ++i) {
if (notePrevPressed[i]) {
addEvent(endTime, i, false);
notePrevPressed[i] = false;
noteHeld[i] = false;
}
}

if (endTime < 10) endTime = 10;
loopLength = endTime;
playStartMs = millis();
mode = MODE_PLAYING;
stopTone();
}

void stopAndClearLoop() {
mode = MODE_IDLE;
eventCount = 0;
stopTone();
for (int i = 0; i < 7; ++i) {
noteHeld[i] = false;
notePrevPressed[i] = false;
}
}

// ----- Playback engine with overdub support -----
// During PLAYING:
// - We rebuild state[] from all events with t <= pos
// - We OR in any live-held notes (for immediate play)
// - When you hit pads, we also insert new events at current pos
void playbackUpdate() {
if (mode != MODE_PLAYING || eventCount == 0) {
// Still allow live play even if no events yet
bool stateLive[7];
for (int i=0; i<7; ++i) stateLive[i] = noteHeld[i];
int liveNote = pickHeldNote(stateLive);
if (liveNote < 0) {
if (currentNote != -1) stopTone();
} else {
if (currentNote != liveNote) startToneFor(liveNote);
}
return;
}

uint32_t now = millis();
uint32_t pos = (now - playStartMs) % loopLength;

bool state[7] = {false,false,false,false,false,false,false};

// Apply recorded events up to current position
for (uint16_t i = 0; i < eventCount; ++i) {
if (events[i].t > pos) break;
state[events[i].note] = events[i].on;
}

// Overlay live-held notes (overdub play on top)
for (int i = 0; i < 7; ++i) {
if (noteHeld[i]) state[i] = true;
}

int noteToPlay = pickHeldNote(state);

if (noteToPlay < 0) {
if (currentNote != -1) stopTone();
} else {
if (currentNote != noteToPlay) startToneFor(noteToPlay);
}
}

// ----- Setup & main loop -----
void setup() {
// Buttons as inputs with pull-up
for (int i = 0; i < 7; ++i) {
pinMode(NOTE_PINS[i], INPUT_PULLUP);
}
pinMode(PIN_LOOP, INPUT_PULLUP);
pinMode(PIN_OCTAVE, INPUT_PULLUP);

stopTone();

// Startup beep so you know PIN_BUZZ + wiring works
tone(PIN_BUZZ, 1000); delay(120); stopTone(); delay(80);
tone(PIN_BUZZ, 1500); delay(120); stopTone();
}

void loop() {
uint32_t now = millis();

// --- LOOP button (D7) ---
bool loopNow = (digitalRead(PIN_LOOP) == LOW);
if (loopNow && !loopPrevPressed) {
if (mode == MODE_IDLE) startRecording();
else if (mode == MODE_RECORDING) startPlayback();
else stopAndClearLoop();
}
loopPrevPressed = loopNow;

// --- OCTAVE button (D8) ---
bool octNow = (digitalRead(PIN_OCTAVE) == LOW);
if (octNow && !octPrevPressed) {
currentOctave++;
if (currentOctave > MAX_OCTAVE) currentOctave = 0;
}
octPrevPressed = octNow;

// --- NOTE buttons: handle presses/releases in ALL modes ---
for (int i = 0; i < 7; ++i) {
bool pressedNow = (digitalRead(NOTE_PINS[i]) == LOW);

// Just pressed
if (pressedNow && !notePrevPressed[i]) {
noteHeld[i] = true;

if (mode == MODE_RECORDING) {
addEvent(now - recStartMs, i, true);
} else if (mode == MODE_PLAYING && loopLength > 0) {
// overdub ON at current loop position
uint32_t pos = (now - playStartMs) % loopLength;
addEvent(pos, i, true);
}
}

// Just released
if (!pressedNow && notePrevPressed[i]) {
noteHeld[i] = false;

if (mode == MODE_RECORDING) {
addEvent(now - recStartMs, i, false);
} else if (mode == MODE_PLAYING && loopLength > 0) {
// overdub OFF at current loop position
uint32_t pos = (now - playStartMs) % loopLength;
addEvent(pos, i, false);
}
}

notePrevPressed[i] = pressedNow;
}

// --- Output sound depending on mode ---
if (mode == MODE_IDLE || mode == MODE_RECORDING) {
int liveNote = pickHeldNote(noteHeld);
if (liveNote < 0) {
if (currentNote != -1) stopTone();
} else {
if (currentNote != liveNote) startToneFor(liveNote);
}
} else if (mode == MODE_PLAYING) {
playbackUpdate();
}
}

Conclusion

MiniBeeps pose pic.JPG
MiniBeeps finished.JPG

this was a very cool project for me to make and i loved making it because i like music and just playing little notes. i hoped you had a good time to! enjoy your MiniBeeps!

play it with the buzzer facing you

there are some extra functions as well if you hit the bottom left button it will switch octaves and if you hit the one up from it, it will start the loop function witch then if you play notes then hit it again it will keep playing those notes and you can add onto it! to stop your loop just tap it again then it will stop playing.