Servo Motors & Buzzers
By the end of this lesson you will be able to turn a servo motor to any angle from 0 to 180 degrees, control it with a knob, play musical notes on a buzzer, and make the parking assistant from the previous lesson beep for real. These are your first actuators that move and make sound.
A servo is the motor inside a remote-control car's steering, a toy robot arm or an automatic pet feeder. A buzzer is the beeper in your microwave, inverter and washing machine.
Part 1: Servo motors
A servo (hobby servo) is a small motor with gears, a position sensor and a control circuit in one box. You do not tell it "spin"; you tell it "go to 90 degrees and stay there". The blue SG90 micro servo is in almost every kit.
| Wire colour (SG90, usual) | Job | Connects to |
|---|---|---|
| Brown | GND | Ground of the servo supply, joined to Arduino GND |
| Red | Power, about 4.8 to 6 V | 5 V servo supply |
| Orange | Signal | Arduino D9 |
The signal is a pulse repeated about 50 times a second. The width of each pulse, roughly 1 to 2 milliseconds, tells the servo which angle to hold. You do not have to create these pulses yourself: the Servo library does it.
Power the servo properly
A servo draws little current when still, but a few hundred milliamps when it moves, and even more when it is blocked. Those current spikes can make the voltage dip so the Arduino resets, or make the USB port shut down.
| Setup | Advice |
|---|---|
| One SG90, light load, quick test | Often works from the Arduino 5V pin, but resets are possible |
| One servo doing real work, or two or more servos | Use a separate 5 V supply rated at 1 A or more |
| Any external supply | Join its GND to Arduino GND; never connect its + to the Arduino 5V pin |
The common ground is essential. The signal is measured relative to GND, so both sides must agree on what 0 V means, like two people agreeing on the same starting line before measuring a race.
Step 1: Sweep the servo
The Servo library is usually installed with the IDE. If #include <Servo.h> gives an error, install Servo by Arduino from the Library Manager.
#include <Servo.h>
Servo arm; // a Servo object controls one servo
void setup() {
arm.attach(9); // signal wire on pin 9
Serial.begin(9600);
while (!Serial && millis() < 3000) { }
}
void loop() {
int angles[] = {0, 90, 180, 90};
for (int i = 0; i < 4; i++) {
arm.write(angles[i]); // move to this angle
Serial.print("angle ");
Serial.println(angles[i]);
delay(1000); // give it time to get there
}
}angle 0 angle 90 angle 180 angle 90 angle 0
The horn (the plastic arm) should swing to one end, the middle, the other end and back, once per second. If it buzzes at 0 or 180 degrees, your servo cannot quite reach the end. Use 10 and 170 instead.
Step 2: Control it with a knob
Map the potentiometer's 0 to 1023 onto 0 to 180 degrees:
#include <Servo.h>
Servo arm;
int lastAngle = -1;
void setup() {
arm.attach(9);
Serial.begin(9600);
}
void loop() {
int raw = analogRead(A0);
int angle = map(raw, 0, 1023, 0, 180);
if (abs(angle - lastAngle) >= 2) { // ignore tiny jitter
arm.write(angle);
Serial.print("angle:");
Serial.println(angle);
lastAngle = angle;
}
delay(15);
}At the three key knob positions, map() gives 0 → 0, 512 → 90 and 1023 → 180. Turning the knob now steers the servo, just like a steering wheel. Only moving when the angle changes by 2 or more stops the servo from twitching on noisy readings.
Part 2: Buzzers
There are two kinds, and they look almost the same:
| Type | How it works | How to drive it |
|---|---|---|
| Active buzzer | Has its own oscillator inside; beeps at one fixed pitch when given DC | digitalWrite(pin, HIGH) to beep |
| Passive buzzer | A plain sound element; needs a changing signal | tone(pin, frequency) to play any note |
A quick test: connect it briefly to 5V and GND (most modules have a + mark). If it beeps, it is active. If it only clicks, it is passive.
For the UNO R4's 8 mA pin limit, use a buzzer module (a small board with three pins, which usually includes a transistor) or a piezo disc. A bare magnetic buzzer can draw more than a pin should supply; drive it through a transistor.
| Part | Pin | Connects to |
|---|---|---|
| Passive buzzer module | Signal (S or I/O) | Arduino D8 |
| Passive buzzer module | VCC | Arduino 5V |
| Passive buzzer module | GND | Arduino GND |
Step 3: Play the sargam
tone(pin, frequency, duration) plays a square wave at that frequency for that many milliseconds. noTone(pin) stops it. Here are the eight notes of a sargam (Sa Re Ga Ma Pa Dha Ni Sa), using Western note frequencies rounded to whole hertz, starting from middle C:
const int BUZZER_PIN = 8;
const char* names[] = {"Sa", "Re", "Ga", "Ma", "Pa", "Dha", "Ni", "Sa'"};
const int freqs[] = {262, 294, 330, 349, 392, 440, 494, 523};
void setup() {
Serial.begin(9600);
while (!Serial && millis() < 3000) { }
for (int i = 0; i < 8; i++) {
Serial.print(names[i]);
Serial.print(" = ");
Serial.print(freqs[i]);
Serial.println(" Hz");
tone(BUZZER_PIN, freqs[i], 400); // play for 400 ms
delay(450); // a small gap between notes
}
noTone(BUZZER_PIN);
}
void loop() {
}Sa = 262 Hz Re = 294 Hz Ga = 330 Hz Ma = 349 Hz Pa = 392 Hz Dha = 440 Hz Ni = 494 Hz Sa' = 523 Hz
You should hear a rising scale. The upper Sa is about double the frequency of the lower one (523 is about 2 × 262), which is what an octave means. A tiny buzzer is no harmonium, but the tune is recognisable.
tone() with a duration does not wait: it starts the sound and returns at once. That is why the sketch needs delay(450) to let each note finish.
Step 4: The parking assistant beeps
Combine the beep-gap function from Ultrasonic Distance & PIR Motion Sensors with millis() timing, so the beeping never blocks the distance readings:
const int BUZZER_PIN = 8;
unsigned long lastBeep = 0;
long beepGapMs(float cm) {
if (cm < 0) return -1;
if (cm < 10) return 0;
if (cm < 30) return 150;
if (cm < 60) return 400;
return -1;
}
void beepFor(float cm) {
long gap = beepGapMs(cm);
unsigned long now = millis();
if (gap < 0) {
noTone(BUZZER_PIN); // far away: silent
} else if (gap == 0) {
tone(BUZZER_PIN, 1000); // very close: continuous tone
} else if (now - lastBeep >= (unsigned long)gap) {
lastBeep = now;
tone(BUZZER_PIN, 1000, 60); // short beep
}
}Call beepFor(readDistanceCm()) from loop() every 60 ms or so. As your hand approaches, you will hear silence, slow beeps, fast beeps and finally a continuous tone, exactly like a car's rear sensor.
Common mistakes
- Powering several servos from the Arduino. The board resets or behaves strangely. Use a separate supply with a common GND.
- No common GND between servo supply and Arduino. The servo jitters or ignores commands.
- Expecting a servo to spin continuously. Standard servos only turn to an angle. "Continuous rotation" servos exist, but they cannot hold a position.
- Using
tone()on an active buzzer. It already has its own pitch; just switch it on and off. - Forgetting that
tone()returns immediately. Add a delay or usemillis()before the next note.
Key points
- A hobby servo moves to an angle given by its pulse width;
Servo.attach(pin)andwrite(angle)control it. - Servos need a solid 5 V supply; with an external supply, always join the grounds.
- Active buzzers beep on DC; passive buzzers need
tone(pin, frequency, duration). - Use buzzer modules or a transistor so the pin stays within its current limit.
- Combine actuator control with
millis()so sounds and movements never block sensing.
Exercise
Build a "door chime" for a shop: when a button on pin 2 (INPUT_PULLUP) is pressed, play Sa, Ga, Pa (262, 330, 392 Hz, 250 ms each) and swing a servo on pin 9 from 0 to 90 degrees as a waving flag, then back to 0. Print Welcome! each time. Use edge detection so holding the button plays the chime only once.
Show solution
#include <Servo.h>
const int BUTTON_PIN = 2;
const int BUZZER_PIN = 8;
Servo flag;
int lastState = HIGH;
void chime() {
const int notes[] = {262, 330, 392};
for (int i = 0; i < 3; i++) {
tone(BUZZER_PIN, notes[i], 250);
delay(280);
}
}
void setup() {
pinMode(BUTTON_PIN, INPUT_PULLUP);
flag.attach(9);
flag.write(0);
Serial.begin(9600);
while (!Serial && millis() < 3000) { }
}
void loop() {
int state = digitalRead(BUTTON_PIN);
if (state == LOW && lastState == HIGH) { // just pressed
Serial.println("Welcome!");
flag.write(90);
chime();
flag.write(0);
}
lastState = state;
delay(30);
}Pressing the button twice prints:
Welcome! Welcome!
The delay(30) acts as a simple debounce. Blocking delays are acceptable here because nothing else needs to run during the short chime.
Quick Quiz
1. What does arm.write(90) do to a standard servo?
Standard servos move to an angle and hold it.
2. Why must an external servo supply share GND with the Arduino?
Without a common ground, the servo cannot read the signal correctly.
3. How do you play a 440 Hz note for half a second on a passive buzzer on pin 8?
tone(pin, frequency, duration) plays a note of that frequency for that many milliseconds.
4. An unknown buzzer beeps when connected straight to 5 V and GND. What type is it?
Active buzzers have a built-in oscillator, so they beep on plain DC.
Learn IoT & Arduino with a mentor
Free lessons get you started. Our IoT & Embedded Systems training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
