Music box on an Arduino Uno
A tune on an 8 ohm speaker through 100 ohms, a continuous-rotation servo turning the drum, and a coin vibration motor on a transistor buzzing on the beat. The whole build, an Arduino Uno and 7 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.
Teaching? Assign it to your class in one click.
Intermediate About 30 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $34. On Mokxi they're free.
What you'll learn
- Making a note by toggling a pin to form a square wave
- Why a speaker needs a series resistor
- Continuous-rotation servos, where the signal sets speed
- Switching a motor with a transistor and a protective diode
- Difficulty: Intermediate
- Time: About 30 minutes
- Board: Arduino Uno R3
- Parts: 9
How it works
Three outputs each move something. An 8 ohm speaker on pin 8, through a 100 ohm resistor, plays Twinkle, twinkle as a square wave. A continuous-rotation servo on pin 9 turns a drum. A vibration motor on pin 6 buzzes on the first beat of every bar.
The servo is told a speed, not an angle: 90 is stopped, and 100 is a slow turn. After each pass of the tune, the sketch flips the servo between 100 and 80, so the drum turns the other way. The vibration motor runs from the 3.3 V pin through a transistor and a 1k resistor, with a diode across it.
Parts list
9 parts, plus the jumper wires. They retail for about $34; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Half breadboard
- 1 × Transistor (NPN or PNP)In LED Cube Kit.
- 1 × Resistor, 1k ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × DiodeIn Pinball Table Kit.
- 1 × Vibration motor
- 1 × Resistor, 100 ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × Speaker, 8 ohmIn Halloween Prop Kit, Christmas Lights Kit, Laser Sword Kit.
- 1 × Hobby servoIn Halloween Prop Kit, Christmas Lights Kit, Starter Projects Kit.
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| Hobby servo pin PWM | Arduino Uno R3 pin 9 |
| Resistor, 100 Ω pin 1 | Arduino Uno R3 pin 8 |
| Resistor, 1k Ω pin 1 | Arduino Uno R3 pin 6 |
| Diode pin C | Arduino Uno R3 pin 3V3 |
| Vibration motor pin 1 | Arduino Uno R3 pin 3V3 |
| Hobby servo pin VCC | Arduino Uno R3 pin 5V |
| Transistor (NPN or PNP) pin E | Arduino Uno R3 pin GND |
| Speaker, 8 ohm pin 2 | Arduino Uno R3 pin GND |
| Hobby servo pin GND | Arduino Uno R3 pin GND |
| Resistor, 1k Ω pin 2 | Transistor (NPN or PNP) pin B |
| Diode pin A | Transistor (NPN or PNP) pin C |
| Vibration motor pin 2 | Transistor (NPN or PNP) pin C |
| Speaker, 8 ohm pin 1 | Resistor, 100 Ω pin 2 |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Music box: a tune, a turning drum, and a buzz on the beat.
//
// pin 8 the speaker, through 100 ohms (its other tag to GND)
// pin 9 the continuous-rotation servo's signal
// pin 6 the vibration motor's transistor, through 1 k to its base
//
// Three outputs that all move something, and each needs driving differently.
//
// The speaker is eight ohms. A pin across eight ohms is nearly a short, so it
// goes through 100 ohms, as in the Arduino's own melody example: about 46 mA,
// and a note you can hear. The note is a square wave made the way mokxiTone
// makes one: toggle the pin, wait, for as long as the note lasts.
//
// The servo is the continuous kind: the pulse is a speed, not an angle. 90 is
// stopped, 180 is full speed one way, 0 the other. 100 is a slow turn, and the
// servo keeps it until it is told something else.
//
// The vibration motor wants 75 mA and at least 2.3 V to start, so a 2N2222
// switches it off the Uno's 3.3 V pin, near its rated 3 V, with a diode across
// it for the kick a real motor gives when its current stops.
#include "mokxi_servo.h"
#include "mokxi_tone.h"
const int SPEAKER = 8;
const int DRUM = 9;
const int BUZZ = 6;
Servo drum(DRUM);
bool forwards = true;
// Twinkle, twinkle: a note and its length in eighths.
const uint16_t NOTES[] = {NOTE_C4, NOTE_C4, NOTE_G4, NOTE_G4, NOTE_A4, NOTE_A4, NOTE_G4,
349, 349, NOTE_E4, NOTE_E4, 294, 294, NOTE_C4};
const uint8_t LENGTHS[] = {2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 4};
const int COUNT = sizeof(LENGTHS);
const uint16_t EIGHTH_MS = 150;
// Half a cycle of the note: the pin to one level, then a wait. Out of line on
// purpose. Inlined, delayMicroseconds() makes the loop below too long for the
// AVR's short branches and the link fails; as a call it is one rcall.
__attribute__((noinline)) void half(uint8_t level, uint16_t us) {
digitalWrite(SPEAKER, level);
delayMicroseconds(us);
}
// One note of `ms` milliseconds: what mokxiTone does, a square wave made by
// toggling the pin and waiting, spelled out here so it can be read.
void play(uint16_t hz, uint16_t ms) {
uint16_t us = (uint16_t)(500000uL / hz);
uint32_t cycles = ((uint32_t)ms * 1000uL) / (2uL * us);
for (uint32_t i = 0; i < cycles; i++) {
half(HIGH, us);
half(LOW, us);
}
}
void setup() {
pinMode(BUZZ, OUTPUT);
digitalWrite(BUZZ, LOW);
pinMode(SPEAKER, OUTPUT);
Serial.begin(115200);
Serial.println("Mokxi Uno: a music box");
drum.begin();
drum.write(100); // a slow turn
}
void loop() {
for (int i = 0; i < COUNT; i++) {
// A buzz on the first beat of every bar.
digitalWrite(BUZZ, i % 4 == 0 ? HIGH : LOW);
play(NOTES[i], LENGTHS[i] * EIGHTH_MS - 30);
digitalWrite(BUZZ, LOW);
delay(30);
}
forwards = !forwards;
Serial.println(forwards ? "once more, forwards" : "once more, the drum the other way");
drum.write(forwards ? 100 : 80);
delay(600);
}
Code walkthrough
The key functions of the sketch, and what each one does.
play sketch.ino
Plays one note. It turns the frequency into half a cycle in microseconds, then works out how many full cycles fit in the note's length. Then it raises and lowers the speaker pin that many times, waiting half a cycle each time.
// One note of `ms` milliseconds: what mokxiTone does, a square wave made by
// toggling the pin and waiting, spelled out here so it can be read.
void play(uint16_t hz, uint16_t ms) {
uint16_t us = (uint16_t)(500000uL / hz);
uint32_t cycles = ((uint32_t)ms * 1000uL) / (2uL * us);
for (uint32_t i = 0; i < cycles; i++) {
half(HIGH, us);
half(LOW, us);
}
}setup sketch.ino
Makes the buzz and speaker pins outputs, with the buzz off. It starts the servo with begin and writes 100 so the drum turns slowly.
void setup() {
pinMode(BUZZ, OUTPUT);
digitalWrite(BUZZ, LOW);
pinMode(SPEAKER, OUTPUT);
Serial.begin(115200);
Serial.println("Mokxi Uno: a music box");
drum.begin();
drum.write(100); // a slow turn
}loop sketch.ino
Steps through the tune. The buzz is on for every fourth note, the note plays for its length times EIGHTH_MS minus 30 ms, and a 30 ms gap separates notes. At the end it flips forwards, changes the servo between 100 and 80, and pauses 600 ms.
void loop() {
for (int i = 0; i < COUNT; i++) {
// A buzz on the first beat of every bar.
digitalWrite(BUZZ, i % 4 == 0 ? HIGH : LOW);
play(NOTES[i], LENGTHS[i] * EIGHTH_MS - 30);
digitalWrite(BUZZ, LOW);
delay(30);
}
forwards = !forwards;
Serial.println(forwards ? "once more, forwards" : "once more, the drum the other way");
drum.write(forwards ? 100 : 80);
delay(600);
}Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- The speaker is very quiet or the pin runs hot This project
Keep the 100 ohm resistor in series with the speaker. Never connect 8 ohms straight to a pin. - The drum never stops or only turns one way This project
Check that the servo is the continuous-rotation kind. A normal servo treats 100 as an angle. - The load never turns on, or is always on, because the legs are in the wrong order. Transistor (NPN or PNP)
Check the pinout in the datasheet for your part. A 2N2222 or BC547 can have emitter, base, collector in a different order. Flat side orientation is easy to get wrong. - The base is wired straight to the pin with no resistor, or the motor has no flyback diode. Transistor (NPN or PNP)
Put about 1k between the pin and the base. For motors and relays, add a diode across the coil with the stripe toward the positive side. - The LED is dim, burns out, or the pin reads wrong because the resistor value is off. Resistor
Read the color bands again. A 220 ohm to 330 ohm resistor suits a typical LED on 5 V. 10k is a common pull-up or pull-down value. Check with a multimeter if unsure. - A pull-up or pull-down resistor is missing, so the input floats and reads random values. Resistor
Tie the input to 5 V or ground through about 10k. Or use INPUT_PULLUP and wire the switch to ground. - The diode is in backwards, so nothing flows, or a flyback diode conducts all the time. Current flows from the anode to the cathode, the end with the stripe. Diode
Put the striped end toward the more positive side for a series diode. For flyback across a coil, the stripe goes to the positive supply side of the coil. - A diode in series drops about 0.7 V (silicon) and 0.3 V (Schottky), so the output is lower than expected. A 1N4148 is also too small for big currents. Diode
Count the drop in your voltage budget. Use a 1N4007 or similar for amps, and a Schottky when you need a small drop.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.