Metronome on an Arduino Uno
The Uno clicks a beat on a piezo and two buttons walk the tempo between 40 and 240 bpm. The whole build, an Arduino Uno and 5 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
BeginnerRuns in your browser. Free, and no account needed.
Step by step, with the wiring and the common mistakes: Arduino Buzzer and Piezo: Beeps, Notes, Metronome
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Metronome: a click a beat on the piezo, with the tempo on two buttons.
//
// pin 7 a passive piezo straight to GND
// pin 9 the beat lamp, through 220 ohm to GND
// pin 2 faster, to GND pin 3 slower, to GND
//
// The tempo is kept as beats per minute and the gap between beats is worked
// out from it: 60 000 milliseconds in a minute divided by the tempo. All of it
// is integer arithmetic, because there is no soft-float runtime here: 60 000
// over 120 is 500, and the remainder nobody can hear.
//
// The first beat of every bar of four is a higher note, which is what a real
// metronome's bell does and what makes it usable: you can hear where you are
// in the bar without counting.
//
// The wait between beats is where the buttons are read, so the tempo changes
// on the next beat rather than the next press. The click itself blocks for its
// eight milliseconds, so the gap is shortened by that much and a beat lands
// where it should.
#include "mokxi_tone.h"
const int PIEZO = 7;
const int LAMP = 9;
const int FASTER = 2;
const int SLOWER = 3;
const int MIN_BPM = 40;
const int MAX_BPM = 240;
const unsigned long CLICK_MS = 8;
int bpm = 96;
int beat = 0;
static bool down(int pin) { return digitalRead(pin) == LOW; }
static void say() {
Serial.print("tempo ");
Serial.print(bpm);
Serial.println(" bpm");
}
// Watch the buttons for `ms`, changing the tempo on an edge.
static void waitAndWatch(unsigned long ms) {
static bool wasFaster = false;
static bool wasSlower = false;
unsigned long start = millis();
while (millis() - start < ms) {
bool f = down(FASTER);
bool s = down(SLOWER);
if (f && !wasFaster && bpm < MAX_BPM) {
bpm += 4;
say();
}
if (s && !wasSlower && bpm > MIN_BPM) {
bpm -= 4;
say();
}
wasFaster = f;
wasSlower = s;
delay(4);
}
}
void setup() {
pinMode(LAMP, OUTPUT);
pinMode(PIEZO, OUTPUT);
pinMode(FASTER, INPUT_PULLUP);
pinMode(SLOWER, INPUT_PULLUP);
Serial.begin(115200);
Serial.println("Mokxi Arduino Uno: metronome");
Serial.println("one button speeds it up, the other slows it down");
say();
}
void loop() {
unsigned long gap = 60000UL / (unsigned long)bpm;
digitalWrite(LAMP, HIGH);
mokxiTone(PIEZO, beat == 0 ? NOTE_C6 : NOTE_C5, (uint16_t)CLICK_MS);
digitalWrite(LAMP, LOW);
beat = (beat + 1) & 3;
waitAndWatch(gap > CLICK_MS ? gap - CLICK_MS : 1);
}
Parts list
7 parts, plus the jumper wires. Every one is in the editor's parts bin.
- 1 × Arduino Uno R3
- 1 × Full-size breadboard
- 1 × Resistor, 220 Ω
- 1 × LED, green
- 2 × Pushbutton
- 1 × Piezo speaker
How it is wired
6 connections, pin by pin, read from the circuit itself. Each line is one set of pins joined together, by a jumper wire or a breadboard strip.
- Ground: Arduino Uno R3 pin GND; LED, green pin C; Pushbutton (1) pin 2a; Pushbutton (2) pin 2a; Piezo speaker pin 2
- Arduino Uno R3 pin 9; Resistor, 220 Ω pin 1
- Arduino Uno R3 pin 7; Piezo speaker pin 1
- Arduino Uno R3 pin 3; Pushbutton (2) pin 1a
- Arduino Uno R3 pin 2; Pushbutton (1) pin 1a
- Resistor, 220 Ω pin 2; LED, green pin A
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.