Fading LEDs using PWM with GPIO Zero on the Raspberry Pi

fading_leds

The project

GPIO Zero, the new GPIO library from the Raspberry Pi Foundation, is becoming my go-to method for doing simple things with the Pi’s GPIO pins. Over the past couple of weeks, I’ve been having fun preparing for the Bett exhibition, and more specifically the Raspberry Jam that is being held there on the 23rd January.

Part of my exhibit is a Raspberry Pi Zero with a Scroll pHAT attached. Getting that working was a story in and of itself, but I want to focus today on the 4 LEDs I soldered onto some prototyping board that is attached to the Zero. You can see them in the picture above.

Back to GPIO Zero. It contains a class called PWMLED which allows you to do software PWM with LEDs. What this means is that instead of just blinking them on and off you can fade them in and out. This takes a little bit of doing (unless I’ve missed something) so I thought I’d share with you my method. One thing to understand is that I’ve done it in a ‘thread’, which means that while the Scroll pHAT is doing it’s job of displaying messages, I can have the LEDs doing something disconnected from that process. The individual functions, however, could be removed from inside the threading ‘object’ if you don’t need to do anything else except pulse the LEDs.

Here’s a video to prove that it works!

The code

Anyhoo. Here’s the code. (I’ve removed anything to do with the Scroll pHAT) (You can skip this bit and go straight to the bold bit to download the code from Github.

First of all, let’s import the libraries:

import time, threading, random
from gpiozero import LED, PWMLED

Now, let’s define the LEDs:

# Set-up LEDs
led_red = PWMLED(17)
led_yellow = PWMLED(27)
led_green = PWMLED(22)
led_blue = PWMLED(10)

Now, in order to get a smooth fading action, we’re going to want to loop from 0 to 1 using a ‘float’ (decimal) value. (0.1… 0.2… 0.3… 0.4… etc). We can’t do that without a helper function. Note, Python doesn’t seem to do float subtraction very well, so I found I had to round the floats to one decimal place just to make it play nice! If I’m missing something, please leave a comment!

# Helper function to iterate over a float
def frange(start, stop, step):
  i = start
 
  if (start < stop):
    while i <= stop:
      yield i
      i += step
      # For some reason, += doesn't always add an exact decimal, so we have to round the value
      i = round(i, 1)
  else:
    while i >= stop:
      yield i
      i += step
      # For some reason, += doesn't always add an exact decimal, so we have to round the value
      i = round(i, 1)

Now let’s define a class that we can start as part of a new thread:

class RandomLEDs(threading.Thread):
  def __init__(self, threadID, name):
    threading.Thread.__init__(self)
    self.threadID = threadID
    self.name = name

  def run(self):
    while True:
      led_list = [led_blue,led_yellow,led_green,led_red]
      the_led = random.choice(led_list)
      self.fade_in_led(the_led, 0.03)
      time.sleep(0.3)
      self.fade_out_led(the_led, 0.02)
      time.sleep(0.3)

  # PWM the LED value from 0 to 1 (or from 1 to 0) with a 0.1 step
  def fade_in_led(self, led, speed):
    for i in frange(0.0, 1.0, 0.1):
      led.value = i
      time.sleep(speed)

  def fade_out_led(self, led, speed):
    for i in frange(1.0, 0.0, -0.1):
      led.value = i
      time.sleep(speed)

Now, let’s kick the whole thing off:

# Start independent threads
# This one lights up random LEDs
thread1 = RandomLEDs(1, "Thread-1")
thread1.start()

I’m hoping that all the indents have come through okay (I just spent the last 10 minutes putting spaces in, so hopefully so).

TL;DR

If you want to download the code, do the following:

git clone https://github.com/recantha/scroller

And the file you want is pwm_leds.py

You can run it by doing:

python3 pwm_leds.py

Feedback

I’d be very happy to get any feedback you might want to give about my code, and my approach. I expect that there is a much easier way to do this – the float looping deserves a bit of consideration, for instance – but this is what I got working! In any case, I hope you found the post interesting! 🙂

4 comments for “Fading LEDs using PWM with GPIO Zero on the Raspberry Pi

  1. Tony Goodhew
    5 February 2016 at 8:06 pm

    Back in time when I first started to program (1968), using FORTRAN IV, we were told to always use integers for counted loops (variables starting with letter I to N) and scale and convert to get a floating point value.
    Great project and I’ll give it a try as soon as I have a spare moment. Great to have some advice on using threads.

  2. Carl Elkins
    6 February 2016 at 1:00 am

    It’s not python’s fault – binary representation of floating point numbers is inexact, with some common decimal values yielding infinitely long binary representations, which of necessity, get rounded: there are libraries that work around this, at the cost of speed (lots of it – you wouldn’t use them all the time).
    As previously mentioned, this is commonly addressed by using integers (exact representation) for the loop control variables, and then producing your FP values from these. LED brightness is often represented as a percentage (0-100), then scaled in the library to the PWM resolution.

  3. BluePython
    4 May 2016 at 4:21 pm

    Wouldn’t the blink command work? It has built in fade parameters. http://gpiozero.readthedocs.io/en/v1.2.0/api_output.html#led
    PWMLED would still be required(I think).

    • Michael Horne
      5 May 2016 at 9:05 am

      Yes, that would also work. At the time I wrote it, there was no blink on PWMLED.

Leave a Reply

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