EthicsComputerCourses
Join training +91 90532 10052

Servo Motors & Buzzers

IoT & Arduino · Lesson 20 of 29 Intermediate 7 min read Updated

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)JobConnects to
BrownGNDGround of the servo supply, joined to Arduino GND
RedPower, about 4.8 to 6 V5 V servo supply
OrangeSignalArduino 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.

SetupAdvice
One SG90, light load, quick testOften works from the Arduino 5V pin, but resets are possible
One servo doing real work, or two or more servosUse a separate 5 V supply rated at 1 A or more
Any external supplyJoin 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.

C++
#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
  }
}
Output
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:

C++
#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:

TypeHow it worksHow to drive it
Active buzzerHas its own oscillator inside; beeps at one fixed pitch when given DCdigitalWrite(pin, HIGH) to beep
Passive buzzerA plain sound element; needs a changing signaltone(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.

PartPinConnects to
Passive buzzer moduleSignal (S or I/O)Arduino D8
Passive buzzer moduleVCCArduino 5V
Passive buzzer moduleGNDArduino 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:

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() {
}
Output
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:

C++
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 use millis() before the next note.

Key points

  • A hobby servo moves to an angle given by its pulse width; Servo.attach(pin) and write(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
C++
#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:

Output
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?

2. Why must an external servo supply share GND with the Arduino?

3. How do you play a 440 Hz note for half a second on a passive buzzer on pin 8?

4. An unknown buzzer beeps when connected straight to 5 V and GND. What type is it?

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