3. Orangutan Buzzer: Beeps and Music

The OrangutanBuzzer class and the C functions in this section allow various sounds to be played on the buzzer of the Orangutan LV, SV, SVP, X2, and 3pi robot, from simple beeps to complex tunes. The buzzer is controlled using one of the Timer 1 PWM outputs, so it will conflict with any other uses of Timer 1. Note durations are timed using a Timer 1 overflow interrupt (TIMER1_OVF), which will briefly interrupt execution of your main program at the frequency of the sound being played. In most cases, the interrupt-handling routine is very short (several microseconds). However, when playing a sequence of notes in PLAY_AUTOMATIC mode (the default mode) with the play() command, this interrupt takes much longer than normal (perhaps several hundred microseconds) every time it starts a new note. It is important to take this into account when writing timing-critical code.

Note that the Orangutan X2 uses its auxiliary microcontroller to control the buzzer. The firmware on this microcontroller duplicates the play_note() and play_frequency() functionality of this library, so it is possible to play music on the Orangutan X2 without using Timer 1 (see the Section 15 for Orangutan X2 functions). The OrangutanBuzzer library functions call the appropriate functions in the OrangutanX2 library, so they produce the same results on the X2 as they do on the other Orangutans, and they use Timer 1 to achieve the proper note durations.

This library can be used with the Baby Orangutan if one pin of an external buzzer is connected to pin PB2 and the other pin is connected to ground.

For a higher level overview of this library and example programs that show how this library can be used, please see Section 3.b of the Pololu AVR C/C++ Library User’s Guide.

Note: The OrangutanServos and OrangutanBuzzer libraries both use Timer 1, so they cannot be used together simultaneously. It is possible to alternate use of OrangutanBuzzer and OrangutanServos routines, however. The servos-and-buzzer example program shows how this can be done and also provides functions for playing notes on the buzzer without using Timer 1 or the OrangutanBuzzer functions.

Reference

C++ and Arduino methods are shown in red.

C functions are shown in green.

static void OrangutanBuzzer::playFrequency(unsigned int frequency, unsigned int duration, unsigned char volume)

void play_frequency(unsigned int freq, unsigned int duration, unsigned char volume)

This method will play the specified frequency (in Hz or 0.1 Hz) for the specified duration (in ms). The frequency argument must be between 40 Hz and 10 kHz. If the most significant bit of frequency is set, the frequency played is the value of the lower 15 bits of frequency in units of 0.1 Hz. Therefore, you can play a frequency of 44.5 Hz by using a frequency of (DIV_BY_10 | 445). If the most significant bit of frequency is not set, the units for frequency are Hz. The volume argument controls the buzzer volume, with 15 being the loudest and 0 being the quietest. A volume of 15 supplies the buzzer with a 50% duty cycle PWM at the specified frequency. Lowering volume by one halves the duty cycle (so 14 gives a 25% duty cycle, 13 gives a 12.5% duty cycle, etc). The volume control is somewhat crude and should be thought of as a bonus feature.

This function plays the note in the background while your program continues to execute. If you call another buzzer function while the note is playing, the new function call will overwrite the previous and take control of the buzzer. If you want to string notes together, you should either use the play() function or put an appropriate delay after you start a note playing. You can use the is_playing() function to figure out when the buzzer is through playing its note or melody.

Example

// play a 6 kHz note for 250 ms at "half" volume
OrangutanBuzzer::playFrequency(6000, 250, 7);

// wait for buzzer to finish playing the note
while (OrangutanBuzzer::isPlaying());

// play a 44.5 Hz note for 1 s at full volume
OrangutanBuzzer::playFrequency(DIV_BY_10 | 445, 1000, 15);
// play a 6 kHz note for 250 ms at half volume
play_frequency(6000, 250, 7);

// wait for buzzer to finish playing the note
while (is_playing());

// play a 44.5 Hz note for 1 s at full volume
play_frequency(DIV_BY_10 | 445, 1000, 15);

Caution: frequency × duration/1000 must be no greater than 0xFFFF (65535). This means you can’t use a max duration of 65535 ms for frequencies greater than 1 kHz. For example, the maximum duration you can use for a frequency of 10 kHz is 6553 ms. If you use a duration longer than this, you will produce an integer overflow that can result in unexpected behavior.

static void OrangutanBuzzer::playNote(unsigned char note, unsigned int duration, unsigned char volume)

void play_note(unsigned char note, unsigned int duration, unsigned char volume);

This method will play the specified note for the specified duration (in ms). The note argument is an enumeration for the notes of the equal tempered scale (ETS). See the Note Macros section below for more information. The volume argument controls the buzzer volume, with 15 being the loudest and 0 being the quietest. A volume of 15 supplies the buzzer with a 50% duty cycle PWM at the specified frequency. Lowering volume by one halves the duty cycle (so 14 gives a 25% duty cycle, 13 gives a 12.5% duty cycle, etc). The volume control is somewhat crude and should be thought of as a bonus feature.

This function plays the note in the background while your program continues to execute. If you call another buzzer function while the note is playing, the new function call will overwrite the previous and take control of the buzzer. If you want to string notes together, you should either use the play() function or put an appropriate delay after you start a note playing. You can use the is_playing() function to figure out when the buzzer is through playing its note or melody.

Note Macros

To make it easier for you to specify notes in your code, this library defines the following macros:

// x will determine the octave of the note
#define C( x )        (  0 + x*12 ) 
#define C_SHARP( x )  (  1 + x*12 ) 
#define D_FLAT( x )   (  1 + x*12 ) 
#define D( x )        (  2 + x*12 ) 
#define D_SHARP( x )  (  3 + x*12 ) 
#define E_FLAT( x )   (  3 + x*12 ) 
#define E( x )        (  4 + x*12 ) 
#define F( x )        (  5 + x*12 ) 
#define F_SHARP( x )  (  6 + x*12 ) 
#define G_FLAT( x )   (  6 + x*12 ) 
#define G( x )        (  7 + x*12 ) 
#define G_SHARP( x )  (  8 + x*12 ) 
#define A_FLAT( x )   (  8 + x*12 ) 
#define A( x )        (  9 + x*12 ) 
#define A_SHARP( x )  ( 10 + x*12 ) 
#define B_FLAT( x )   ( 10 + x*12 ) 
#define B( x )        ( 11 + x*12 ) 

// 255 (silences buzzer for the note duration)
#define SILENT_NOTE   0xFF

// e.g. frequency = 445 | DIV_BY_10
// gives a frequency of 44.5 Hz
#define DIV_BY_10	 (1 << 15)  

static void OrangutanBuzzer::play(const char* sequence)

void play(const char* sequence)

This method plays the specified sequence of notes. If the play mode is PLAY_AUTOMATIC (default), the sequence of notes will play with no further action required by the user. If the play mode is PLAY_CHECK, the user will need to call playCheck() in the main loop to initiate the playing of each new note in the sequence. The play mode can be changed while the sequence is playing. The sequence syntax is modeled after the PLAY commands in GW-BASIC, with just a few differences.

The notes are specified by the characters C, D, E, F, G, A, and B, and they are played by default as “quarter notes” with a length of 500 ms. This corresponds to a tempo of 120 beats/min. Other durations can be specified by putting a number immediately after the note. For example, C8 specifies C played as an eighth note, with half the duration of a quarter note. The special note R plays a rest (no sound). The sequence parser is case-insensitive and ignore spaces, which may be used to format your music nicely.

Various control characters alter the sound:

  • ‘A’ – ‘G’: used to specify the notes that will be played
  • ‘R’: used to specify a rest (no sound for the duration of the note)
  • ‘>’ plays the next note one octave higher
  • ‘<’ plays the next note one octave lower
  • ‘+’ or ‘#’ after a note raises any note one half-step
  • ‘-’ after a note lowers any note one half-step
  • ‘.’ after a note “dots” it, increasing the length by 50%. Each additional dot adds half as much as the previous dot, so that “A..” is 1.75 times the length of “A”.
  • ‘O’ followed by a number sets the octave (default: O4).
  • ‘T’ followed by a number sets the tempo in beats/min (default: T120).
  • ‘L’ followed by a number sets the default note duration to the type specified by the number: 4 for quarter notes, 8 for eighth notes, 16 for sixteenth notes, etc. (default: L4).
  • ‘V’ followed by a number from 0-15 sets the music volume (default: V15).
  • ‘MS’ sets all subsequent notes to play play staccato – each note is played for 1/2 of its allotted time, followed by an equal period of silence.
  • ‘ML’ sets all subsequent notes to play legato – each note is played for full length. This is the default setting.
  • ‘!’ resets the octave, tempo, duration, volume, and staccato setting to their default values. These settings persist from one play() to the next, which allows you to more conveniently break up your music into reusable sections.
  • 1-2000: when immediately following a note, a number determines the duration of the note. For example, C16 specifies C played as a sixteenth note (1/16th the length of a whole note).

This function plays the string of notes in the background while your program continues to execute. If you call another buzzer function while the melody is playing, the new function call will overwrite the previous and take control of the buzzer. If you want to string melodies together, you should put an appropriate delay after you start a melody playing. You can use the is_playing() function to figure out when the buzzer is through playing the melody.

Examples:

// play a C major scale up and back down:
OrangutanBuzzer::play("!L16 V8 cdefgab>cbagfedc");
while (OrangutanBuzzer::isPlaying());
// the first few measures of Bach's fugue in D-minor
OrangutanBuzzer::play("!T240 L8 a gafaeada c+adaeafa >aa>bac#ada c#adaeaf4");
// play a C major scale up and back down:
play("!L16 V8 cdefgab>cbagfedc");
while (is_playing());
// the first few measures of Bach's fugue in D-minor
play("!T240 L8 a gafaeada c+adaeafa >aa>bac#ada c#adaeaf4");

static void playFromProgramSpace(const char* sequence)

void play_from_program_space(const char* sequence)

A version of play() that takes a pointer to program space instead of RAM. This is desirable since RAM is limited and the string must be stored in program space anyway.

Example:

#include <avr/pgmspace.h>
const char melody[] PROGMEM = "!L16 V8 cdefgab>cbagfedc";

void someFunction()
{
  OrangutanBuzzer::playFromProgramSpace(melody);
}
#include <avr/pgmspace.h>
const char melody[] PROGMEM = "!L16 V8 cdefgab>cbagfedc";

void someFunction()
{
  play_from_program_space(melody);
}

static void OrangutanBuzzer::playMode(unsigned char mode)

void play_mode(char mode)

This method lets you determine whether the notes of the play() sequence are played automatically in the background or are driven by the play_check() method. If mode is PLAY_AUTOMATIC, the sequence will play automatically in the background, driven by the Timer 1 overflow interrupt. The interrupt will take a considerable amount of time to execute when it starts the next note in the sequence playing, so it is recommended that you do not use automatic-play if you cannot tolerate being interrupted for more than a few microseconds. If mode is PLAY_CHECK, you can control when the next note in the sequence is played by calling the play_check() method at acceptable points in your main loop. If your main loop has substantial delays, it is recommended that you use automatic-play mode rather than play-check mode. Note that the play mode can be changed while the sequence is being played. The mode is set to PLAY_AUTOMATIC by default.

static unsigned char OrangutanBuzzer::playCheck()

unsigned char play_check()

This method only needs to be called if you are in PLAY_CHECK mode. It checks to see whether it is time to start another note in the sequence initiated by play(), and starts it if so. If it is not yet time to start the next note, this method returns without doing anything. Call this as often as possible in your main loop to avoid delays between notes in the sequence. This method returns 0 (false) if the melody to be played is complete, otherwise it returns 1 (true).

static unsigned char OrangutanBuzzer::isPlaying()

unsigned char is_playing()

This method returns 1 (true) if the buzzer is currently playing a note/frequency or if it is still playing a sequence started by play(). Otherwise, it returns 0 (false). You can poll this method to determine when it’s time to play the next note in a sequence, or you can use it as the argument to a delay loop to wait while the buzzer is busy.

static void OrangutanBuzzer::stopPlaying()

void stop_playing()

This method will immediately silence the buzzer and terminate any note/frequency/melody that is currently playing.

Related Products

Pololu 42×19mm Wheel and Encoder Set
Encoder for Pololu Wheel 42x19mm
Orangutan SVP-1284 Robot Controller (assembled)
Orangutan SVP-1284 Robot Controller (partial kit)
Orangutan SV-168 Robot Controller
Baby Orangutan B-328 Robot Controller
QTR-L-1A Reflectance Sensor (2-Pack)
QTR-L-1RC Reflectance Sensor (2-Pack)
QTR-3A Reflectance Sensor Array
QTR-3RC Reflectance Sensor Array
QTR-1A Reflectance Sensor (2-Pack)
Orangutan LV-168 Robot Controller
Orangutan SVP-324 Robot Controller (partial kit)
QTR-1RC Reflectance Sensor (2-Pack)
Orangutan SV-328 Robot Controller
Pololu 3pi Robot
Baby Orangutan B-48 Robot Controller
QTR-1A Reflectance Sensor
QTR-1RC Reflectance Sensor
Orangutan SVP-324 Robot Controller (assembled)
QTR-8A Reflectance Sensor Array
QTR-8RC Reflectance Sensor Array
Orangutan X2 with VNH3
Log In
Pololu Robotics & Electronics
Shopping cart
(702) 262-6648
Same-day shipping, worldwide
Menu
Shop Blog Forum Support
My account Comments or questions? About Pololu Contact Ordering information Distributors