Sine Pulse LED PWM trick

In short

This tutorial shows how to make a pulsating LED using the PWM capabilities of attiny/atmega chips used by Arduinos.

Demonstration of the effect:


For the curious, read on!


How is it done? The math behind it

The sine wave oscillates between -1 and 1 and has a periodicity of 2π -that is to say, it repeats itself every 2 π. In order to make the LED flash as a sine wave we need to do two things first:

  1. Scale the sine wave so that it oscillates between 0 and 1.
  2. Scale the sine wave so that it completes a full wave starting from 0 and ends at 1.


Scaling the sine wave to oscillate between 0 and 1

The [0;1] range holds some very useful properties. Namely, you can multiply this range with a scalar and it will return a value between 0 and this scalar. In the case of the LED trick, we want ultimately to write a PWM value for the pin attached to the LED.

PWM values range from 0 (always off) to 255 (always on). In between, the LED with gradually lighten up from fully turned off to fully turned on. See the Arduino reference.

So, if we can scale the Sine wave to [0;1] instead of [-1;-1], we can simply multiply the value given by 255 and that will give the right value to use for PWM.

Step 1: divide the sine wave by 2 so that it oscillate between -0.5 and 0.5

Step 2: add 0.5 so that it oscillate between 0 and 1.

Voila! Easy enough isn’t it? That’s only half of the problem though, because we still have a sine wave of a 2π periodicity (that is to say about 6.28 long). This is not really useful if you want your LED to flashes at about 1 second frequency.

Scale the sine wave so that it completes a full wave starting from 0 and ends at 1.

Scaling the periodicity of the sine wave is a bit trickier and you need some understanding of trigonometry function. From the previous operation, when the sine was shifted up by 0.5, you can now see that the function now doesn’t start at 0; but 0.5. In order to shift the sine wave so that the function’s value is 0 at 0, the key here is to shift it by -π/2. The reason being sin(-pi / 2) equals -1; and since we divided the sine early by 2, 0.5*sin(-π/2) = -0.5.

Finally, since the wave function ranges from 0 to 2π, we can scale it back to 0;1 by adding this 2π scalar to x; as in:

And there you have it! A full wave that starts from 0 and ends at 1, and oscillates between 0 and 1!

Here’s an animated GIF of the process of scaling the sine wave:

Arduino code

The Arduino code is just a direct translation of the function above.

void sin_pwm(const int pin_number, const unsigned long timer, const unsigned int len){
    //PWM modulation
    unsigned long mod = timer % len;
    //0.5 * (1 + sin(2*pi*x - pi/2))
    float f = 0.5f * (1.0f + sin(   2.0f * PI * (mod/(float)len) - PI * 0.5f));
    analogWrite(pin_number, (unsigned char) (f * 255.0f));

The complete code used with an ATTINY85 in the video above can be downloaded at GitHub. Schematics used below:

If you want to use the same code using an Arduino, remember that you can only use PWM pins. On an Arduino Uno you can use the pins 11, 10, 9, 6, 5 and 3. This puny ATTINY above only has two pins usable (namely 0 and 1).


Going further

There are a two issues I would like to address in the code above:

  • It uses floating point numbers. Using a floating point 32 bit math on a 8 bit microcontroller is very expensive. While this should run in all your projects without any problem, a better and faster version of this effect can probably be achieved using 16 bit fixed point numbers.
  • You cannot introduce lots of “delays” calls in your loop, or the animation will become choppy. A few milliseconds here and there would still be ok.


Leave a comment

Your email address will not be published. Required fields are marked *