Monday, July 25, 2011

Arduino Piezo buzzer project with personal method

This is a small project I set up with my Arduino Microcontroller. There are a couple of cool things I learned and realized from this buzzer project. I mainly figured out how to generate a tone with the buzzer in a "manual," custom sort of way, without using the basic tone() function.

I learned how piezo buzzers made sound and how I could reproduce it through a couple of resources.

Colin Cunningham's "Colin's Lab" videos include his piezo crystal video, where he grows his own crystals for piezo material and then hits them to produce current. He briefly explains the reverse would make the crystal move.

I was further helped by these two videos, where "cmhiekses" shows how he made his stepper motors play music. Stepper motors are very precise motors that are turned in a mechanical, clock like way. Therefore he was able to move and stop them for very short amounts of time, creating tones.

I had recently learned more about the physics of making sound. Pretty much anything that vibrates the air or pushes on it at higher frequencies makes a sound or a tone, including small or basic mechanical sounds that are repeated quickly.

What I did
I could make "mechanical movements" by giving power to a piezo buzzer, which would alter it at sort of a molecular level. If done repeatedly at a high frequency, these movements would make a sound. So I pulsed power to a piezo buzzer I got from an old electronics kit, using my Arduino. I added a potentiometer to control the pitch.


I thought this was cool because I was creating the sound almost all by myself. I got it to work using the tone() function in the Arduino library, but then turned the pin on and off myself in the code.

In the video above, you can see the blue plastic part (which came with the buzzer) is like a cone that amplifies the sound of the piezo element inside. Without the cone, I found that the sound would be louder even if I just put the element down on the table, letting the flat surface amplify the sound.

Trying it out on an oscilloscope
I wanted to try hooking this up to an oscilloscope just to see what it would look like. I know someone who has one and they let me use it quickly. I was quite surprised at what I saw. Since I'm just turning the pin on and off with simple timing, you would think it would look like a basic square wave, like so:

But in fact it looked like the one below, when the leads were connected in parallel with the piezo speaker.
I'm not exactly sure why it looks this way. I'm guessing it has something to do with the fact that a piezo element will give off current when "moved," as Colin's video above shows. So if the current from the Arduino modifies the element (or it gets modified because it settles down to its original state when the pin is off), it gives off some extra current that show up as the spikes. That would be something like back emf on motors or inductors. I don't know, when I work that out and what it might look like in my head, it doesn't match up. And I don't really know why it would spike so far down in the "off" sections.

The code
The code listens to the potentiometer and translates its value (with a linear equation) to an appropriate tone. It calculates the period of the tone (frequency is how often the repeating event repeats, ex. times per second, where the period is how long one cycle or event is, ex. time it takes for the event to start doing the same thing again), then turns the pin on and off for half a period each.

The conversion
Someone asked me how I figured out the scaling/multiplying factor/the linear equation. Technically you could use some algebra, but I just figured it out in my own unique way. (That's the cool part about understanding the math to make things work.)
First I searched for a note and frequency table online and found some reasonable frequencies I could play that would actually make sounds (2 Hz clearly won't make much of a sound, while I think 20,000Hz is the max for human hearing). I thought between 3500 Hz and 650 Hz was OK. The period is the frequency inverted (so, on a calculator, period = 1 / freq). So the periods would range from 300-6000 microseconds (I made these nice, even numbers since in practice the period is really what I care about).
For the conversion, I had 0-1023 from the analog input and I wanted 300-6000. So, I could multiply the analog input by some scaling factor and make it equal 0-XX, then add 300 to make it 300-6000. Well XX here would be 6000 subtract 300 = 5700 (I'm working backwards here). I divided 5700/1023 to find the scaling factor, which is 5.57184751*. So my equation is (analog input) * 5.57184751 + 300 = new, scaled value(s).

If you're fresh on your middle school algebra, you could treat the IN and OUT values as points on a graph, or points in a function. Then you can use the simple method to find the slope, plug in a point to find the y intercept (which is clearly 300 here anyways), or plug into point-slope form and convert into slope-intercept form.
Or if you're lazy, you could use WolframAlpha, an online computational search engine from Wolfram Research.

Here is the code: (zoom to see it much better)
Please note that this can be better done with the tone() function (example tutorial), but that my code is a custom method that I used for personal experimentation. I believe some people are copying and using my code for basic buzzer purposes, which is fine, but certainly not the normal method for learning or doing something basic!

 * Custom Tone 4
 * Ben L
 * 08/14/2011
 * Creates a tone on a pezio buzzer in a custom way without the tone() function.
 * This version uses the adequate delayMicroseconds() fuction, instead of the delay()
 * function. It also has an analog input (potentiometer). It uses the more accurate
 * conversion constant, 5.57184751, rather than the previous, mismatched 4.86510264.
 * This program "pulses" the buzzer's power by rapidly turning voltage on and off.
 * This vibrates the buzzer at frequencies capable of producing sound.
 * The pitch can be set with analog input 0.
 * The analog input is converted into the period of a reasonable frequency to play
 * on the buzzer, then the period is halved. The buzzer's digital pin is turned
 * both on and off, each with the length of the halved period. The tone will be a 
 * square wave with a 50% duty cycle.
 * Analog input range is 0-1023,
 * period can be between 300 and 6000 microseconds,
 * frequency can be between 3333Hz and 166Hz
 * e.x. the note A4 has a period of 2272 microseconds and a frequency of 440Hz
 * Setup: wire a pezio buzzer to digital pin 8 and ground. Wire a potentiometer
 * to analog pin 0 (middle lead goes to pin 0, side leads go to power and ground;
 * if the pezio buzzer has a middle or third lead, just ignore it).

double period;
double wait;

void setup()
  pinMode(8, OUTPUT);    //set up DIO pin 8 as an output

void loop()
  //getting sensor reading and calculating
  period = analogRead(0) * 5.57184751 + 300;    //converting
  wait = period / 2;

  //creating sound by "pulsing" power to the pezio buzzer
  digitalWrite(8, HIGH);     //ON
  delayMicroseconds(wait);   //wait 1/2 period
  digitalWrite(8, LOW);      //OFF
  delayMicroseconds(wait);   //wait 1/2 period


  1. all I know on this topic is concert A is 440 not sure the unit...that is what the symphony syncs to before the concert-created by the concertmaster (first chair violin). maybe you could calculate that and create that and see how close it comes to the A on your piano.

  2. Yep, the note A4 has a frequency of 440Hz and a period of 2272 microseconds.

    Thanks, the piano testing idea is a good one. The code itself running might slow it down, so it would be a lower note. But then again, it runs so fast that it might not matter.