At a size that fits on the tip of your finger and a price of about one dollar (depending on quantity and version), the ATTiny85 microcontroller may be the ideal choice for your next small project. In a previous post, we explored how you can program the ATtiny85 with the Arduino IDE, making it much easier to get started. If, however, you want to use the microcontroller to make music, Arduino's tone() function won't work. That means you'll have to find a different way to make your device beep and boop.
Creating Sound with ATTiny85: Beep Loop
If you want to program your board to emit a beeping sound, the process is pretty simple. Producing sound with a speaker is a matter of pulsing electricity at a set interval, which you should be able to accomplish using a loop. Just turn the speaker on and off at a high rate with a delay function. The delayMicroseconds() function between on/off signals will dictate the pitch of the sound:
- Lower pitches = longer time value between pulses
- Higher pitches = shorter time value between pulses
Check out the image below for a step-by-step process to set up a loop and create your own beep:

The ATtiny85 first loops with a delay of 750 microseconds, or 1500 microseconds total per cycle. Next, there's a short delay, followed by chirps at a higher tone with only two 500 microsecond delays. The speaker hooks up to pin 1, as the programmer I'm using has an LED on pin 0.
Doing a little math, 1500 microseconds―or .0015 seconds per cycle―means that the reciprocal will give you a 667 cycles/second or 667 Hertz tone. In musical terms, that tone is close to a G sharp. The second note works out to 1000 Hertz, or roughly a C sharp. You can also create a tone with the standard delay() function, but you'll lose quite a bit of precision in the process.
ATTiny85 Music Coding
If you want a better way to play actual tunes on your ATtiny85, we discovered one excellent method on David Johnson-Davies' site, Technology. You can use this code to play several notes:
- Eight octaves if you're running on a 1MHz clock
- Ten octaves for 8MHz
- Eleven octaves for 16MHz
Best of all, the program detects the frequency that you set up (though it doesn't appear to accommodate 20MHz). Just plug in his code, call the notes, and you'll be jamming to a monophonic beat in no time. Plug your small speaker into pin 1 or 4, and the other lead to ground.
Johnson-Davies outlines how to use his code in the blog post linked above, but he doesn't combine his note definition with the loop code and blank setup() function it requires to run. Fortunately, that combination is easy enough to figure out. Here's how your code should look when it's all put together:

ATTiny85 Sound Applications
The most straightforward application of this technology would be as an alarm for a simple device. On the other hand, you could use it to compose your own custom greeting card. Of course, you'll need a little musical skill and an easily recognizable tune.
You can also use this device for less productive purposes. Given its small size, one might consider pairing the ATtiny85 with a speaker, then hiding it somewhere to "alarm" family, friends, or coworkers. With the ATTiny's power efficiency―it only consumes 300μA in active use, and a fraction of that in sleep mode―this beeping could keep the joke going for weeks or even months.
Code Snippet 1:
//based on code in http://www.technoblogy.com/show?KX0
//heavily modified Jeremy S. Cook 11/13/2019
int Speaker = 1;
void setup()
{
pinMode(Speaker, OUTPUT);
}
void loop() {
for (int i=0; i < 25; i++) {
digitalWrite(Speaker, HIGH);
delayMicroseconds(750);
digitalWrite(Speaker, LOW);
delayMicroseconds(750);
}
delay(50);
for (int i=0; i < 25; i++) {
digitalWrite(Speaker, HIGH);
delayMicroseconds(500);
digitalWrite(Speaker, LOW);
delayMicroseconds(500);
}
delay(2000);
}
Code Snippet 2
//based on code from: http://www.technoblogy.com/show?20MO
//put together by Jeremy S. Cook 11/13/2019
void setup() {}
void loop() {
for (int n=0; n<=12; n++) {
note(n, 4);
if (n!=4 && n!=11) n++;
delay(500);
}
note(0, 0);
delay(10000);
}
const int Output = 1; // Can be 1 or 4
// Cater for 16MHz, 8MHz, or 1MHz clock:
const int Clock = ((F_CPU/1000000UL) == 16) ? 4 : ((F_CPU/1000000UL) == 8) ? 3 : 0;
const uint8_t scale[] PROGMEM = {239,226,213,201,190,179,169,160,151,142,134,127};
void note (int n, int octave) {
int prescaler = 8 + Clock - (octave + n/12);
if (prescaler<1 || prescaler>15 || octave==0) prescaler = 0;
DDRB = (DDRB & ~(1<<Output)) | (prescaler != 0)<<Output;
OCR1C = pgm_read_byte(&scale[n % 12]) - 1;
GTCCR = (Output == 4)<<COM1B0;
TCCR1 = 1<<CTC1 | (Output == 1)<<COM1A0 | prescaler<<CS10;
}

