Sine Waves on Arduino

I’ve spent the last few weeks going over the fundamentals of PWM and implementing them on my Arduino Uno. Today it’s time to apply those basics to something a little more interesting. I want to get my Arduino outputting sine waves. I’m going to focus on creating a workable 10Hz sine wave and then in forthcoming posts I’ll look at manipulating the frequency and modelling more interesting wave-forms.

Know Thy Limits

Before I go to far into this project I want to talk a little bit about the limitations of the Arduino. The Arduino has a maximum PWM frequency of 980Hz which is quite low, especially when working in the audio space. As an example if I wanted a sin wave with a frequency of 100Hz I would only be able to use approximately 10 PWM cycles per sine wave. The fewer cycles you have per wave the more misshapen your output wave will be. I’ve had some success creating “relatively” clean 100Hz waves but going much higher than this results in the wave breaking down entirely.

One nice thing about the sine wave is that the changes in voltage through the wave are continuous. This means we can push our samples much closer than the normal settling time discussed in my last post. However, the idea of creating an full audio oscillator with this method is an impossibility. An LFO or envelope generator though are still realistic goals.

If you are interested in creating an audio oscillator using your Arduino I would recommend looking into off-board DACs (Digital to Analog Converters) which use serial communication with the board to provide much higher output speeds and resolutions.

The Filter

Low Pass Filter With RC=0.0047

To get started I set up an RC Low Pass Filter. After the experiments in my last post I settled on an RC value of 0.005 (or 0.0047 more accurately). This should give me a good balance between stability and low settling time. I used a 47K ohm resistor and a 0.1uF capacitor to accomplish this.

The Sine Wave

Sine Wave Equation with DC Offset

I want to have a quick look at the sine wave equation before I start coding. There are a number of variables which we can use to adjust the waves properties. The first thing to note is the variable n. If you’ve worked with continuous sine waves you may have seen this formula as a function of t. Where t represents time (a continuous variable), n represents discreet samples. This means n will always be a whole number (0, 1, 2, …) which lends itself to working in a digital environment.

The next variable we have to look at is w which represents the angular velocity (also called normalized frequency depending on your background). This represents how quickly your sine wave will move through a full cycle. We can determine the value for w with the following formula:

In the above formula N represents the total number of samples it will take to traverse a full cycle of the sine wave.

Next up is A which is known as the amplitude or scaling factor of the wave and C which represents the offset. A typical sine wave varies between 1 and -1 centered at 0. However, on the Arduino the duty cycle of a PWM signal is set by an integer between 0 and 255. To get our sine wave to fill this range we first multiply the sine wave by 127 (A). This creates a wave which varies between -127 and 127. Next we add the offset of 127 (C) to the output which gives us our final sine values between 0 and 254.

Choosing Your Frequency

Two factors affect the frequency of the sine wave. N (Discussed above) is the first. If you are moving through your samples at a constant rate the more samples you have the longer it will take. The trade off here is as you might imagine, the more samples you have the cleaner your sine wave will look.

Sample time (given in ms) is the other. This is the time the Arduino will wait between each step of the sine wave. This again is something of a balancing act as you must allow enough time for your signal to stabilize at each new voltage level.

Taken together we can create the following formula to determine our final frequency:

Discreet Frequency Calculation

You may recognize that this is essentially the calculation to find frequency from the period (f = 1/p). In this equation N*(sample time) represents the period (the number of samples * the time it takes for each sample). Since sample time is in ms we use 1000 as a conversion factor.

The Code

Now that we’ve got everything set up it’s time to write the code. I’ll start out by declaring the necessary variables:

int PWM_out = 5;           //PWM output at pin5

int sample_time = 5;       //ms
int N = 20;                //int
int duty_cycle;            //unitless

int i;                     //Counter variable

Here you can see I’ve set the sample_time to 5 and N to 20. Using the formula I showed earlier this should give me a frequency of 10Hz (1000/(5*20) = 1000/100 = 10Hz).

For this program I don’t need to run any kind of set-up. That means I can leave the set-up block blank:

void setup() {
}

Next comes our main loop where all the magic happens:

void loop() {
  for (i = 0; i < N; i++){
    duty_cycle = 127+127*sin(i*(2*3.14)/N);
    analogWrite(PWM_out, duty_cycle);
    delay(sample_time);
  }
}

Lets go through this one in a bit more detail. First I set up a for loop which will iterate through the code N times incrementing i each time.

Next I calculate the duty_cycle value using the sine wave equation. Notice I have placed the angular velocity equation directly inside the sine equation (2*pi/N) to simplify the line. From there we write that duty cycle value to the PWM_out pin (pin5) using analogWrite.

Finally there is a delay command. The delay waits for the amount of time defined by sample_time before beginning the next iteration of the loop.

10 Hz Sine Wave From Arduino

And there we have it. As you can see from my oscilloscope output the wave is far from perfect, nonetheless we’ve created an analog sine wave using a digital microcontroller! This is no small accomplishment and I can’t wait to build on it further. I encourage you all to set up this code and play around. Try changing the variables and values to see how each impacts the design. I’ll be back soon with more!