Apr 212013
 
PWM2 trace 3-title

In part 1 of this series, we looked at the basic commands for using software pulse-width modulation (PWM) in RPi.GPIO 0.5.2a and higher. In this article we’ll get a bit more hands-on and into some practical applications for it. It’s all very well being able to make nice square-wave pulses on an oscilloscope, but what’s it actually useful for?

Our servo said? EEEEH AAAAH

I tried using RPi.GPIO soft PWM with servos in response to a query after the last article, but, although it did change the servo positions, it was jittery. Servos require quite precise PWM inputs. These appear to be better suited to hardware PWM solutions (or lower level programming languages). So we’ll leave servos for now and concentrate on controlling the brightness of leds and simple brushed motor speed control.

Leds

The idea here is that you can switch an led on and off very fast and trick the eye into thinking it’s on all the time. Most people can see flickering at or below about 50 Hertz (50 times per second). I remember the “good old days” of CRT monitors. I used to prefer one with a refresh rate of at least 72 Hz to avoid eye strain and headaches. So let’s pick an arbitrary number above 50 Hz, but not too high. Let’s say 100 Hz. That gives us a frequency, which we will leave as it is throughout the program.

Dimming leds using duty cycle

Remembering from last time, the duty cycle is the percentage of time the pulse is “ON”. :)

We will vary the duty cycle to get our variable brightness. A duty cycle value of 0 means fully off, 100 is fully on. Anything in between gives a proportion of full brightness. (Well that’s what your eye thinks. It actually gives full brightness for a proportion of the time.)

The led dimming circuit

Before we can do anything we have to hook it all together. Your leds may need different value resistors than mine, so check.

We have the +ve ends of: a white led connected to GPIO 25; a red led to GPIO 24. White gets a 330R resistor (it’s a superbright led) from -ve to GND. Red gets 56R (diagram shows 68R, which would also be fine) from -ve to GND. GND on the breadboard is connected to pin 6 GND on the Pi.

These leds are taking their power straight from the Pi’s GPIO ports, which is why I’m being careful to restrict the current drawn by my superbright white led.

2 leds dimmed with software PWM in RPi.GPIO

Now we’ve got the circuit sorted out, let’s look at the software.

Program structure

In the program below, we are running a couple of loops which change the duty cycles of red and white leds, such that when one is 100, the other is 0. The first loop (lines 27-30) cycles up to 100 and the second one (31-34) cycles down from 100 to 0 (both from the white led’s point of view).

Those two loops are enclosed in a while True: loop (lines 26-34) that will keep going until you hit CTRL+C. And that while True: loop is inside a try: ... Except KeyboardInterrupt: block that will stop the PWM, clean up the GPIO ports we opened and exit gracefully when we do hit CTRL+C. (Always clean up after yourself ;) ).

#!/usr/bin/env python2.7
# script by Alex Eames http://RasPi.tv
#http://RasPi.tv/2013/how-to-use-soft-pwm-in-rpi-gpio-pt-2-led-dimming-and-motor-speed-control
# Using PWM with RPi.GPIO pt 2 - requires RPi.GPIO 0.5.2a or higher

import RPi.GPIO as GPIO # always needed with RPi.GPIO
from time import sleep  # pull in the sleep function from time module

GPIO.setmode(GPIO.BCM)  # choose BCM or BOARD numbering schemes. I use BCM

GPIO.setup(25, GPIO.OUT)# set GPIO 25 as output for white led
GPIO.setup(24, GPIO.OUT)# set GPIO 24 as output for red led

white = GPIO.PWM(25, 100)    # create object white for PWM on port 25 at 100 Hertz
red = GPIO.PWM(24, 100)      # create object red for PWM on port 24 at 100 Hertz

white.start(0)              # start white led on 0 percent duty cycle (off)
red.start(100)              # red fully on (100%)

# now the fun starts, we'll vary the duty cycle to 
# dim/brighten the leds, so one is bright while the other is dim

pause_time = 0.02           # you can change this to slow down/speed up

try:
    while True:
        for i in range(0,101):      # 101 because it stops when it finishes 100
            white.ChangeDutyCycle(i)
            red.ChangeDutyCycle(100 - i)
            sleep(pause_time)
        for i in range(100,-1,-1):      # from 100 to zero in steps of -1
            white.ChangeDutyCycle(i)
            red.ChangeDutyCycle(100 - i)
            sleep(pause_time)

except KeyboardInterrupt:
    white.stop()            # stop the white PWM output
    red.stop()              # stop the red PWM output
    GPIO.cleanup()          # clean up GPIO on CTRL+C exit

This program will continually oscillate white and red leds between dim and bright until CTRL+C is pressed. When white is bright, red will be dim and vice versa.
You can play with the value of pause_time in line 23 to speed up or slow down the oscillation rate.

Getting the script on your Pi

You can either cut and paste the above code into a nano window on your pi, like this…

nano pwm2.py
cut and paste the above code from this page
CTRL+O
y
CTRL+X

Or, type the following directly from the command line (e.g. LXTerminal) on your Pi…

wget http://raspi.tv/download/pwm2.py.gz
gunzip pwm2.py.gz

Either way, after that you can run it with

sudo python pwm2.py (But you will need RPi.GPIO 0.5.2a or higher)

The video shows what the results should look like


And motors?

The software to control the speed of a motor is exactly the same idea. I’m going to prove that by using the same program and just adding…

  • a couple more wires
  • a Darlington Array (ULN2003) chip
  • a motor
  • a battery to power the motor

Why the Darlington Array?

Most motors need to run at a higher voltage than the Pi’s 3.3V offers, and a higher current than the GPIO ports can cope with. So we need a way of being able to switch a higher voltage with the 3V3 from a GPIO port. The ULN2003 is seven pairs of transistors that allow this switching in a safe way. You could just use a standard transistor, but this chip only costs ~30 pence or 50 cents so why not have the capability of switching 7 things at once – you don’t have to use them all?

The other nice thing this chip does is protect your Pi’s GPIO port from the voltage spike that can occur when an inductive load (a relay, solenoid, motor etc.) is switched off. It has built-in freewheeling diodes that give the required protection – all for much less than the cost of the separate parts. So in my opinion it would be rude not to use one. :)

You can get dedicated H-bridge motor control chips that will give you bidirectional motor control, but for this experiment I wanted to keep things simple and feed the motor raw PWM.

See what the circuit looks like below…

Adding a motor into the previous circuit, using ULN2003 to buffer the Pi

You should now have a pretty good idea how to use RPi.GPIO’s soft PWM to control leds and motors. I hope you found this useful. Let us know what you’re going to use it for in the comments below.

  13 Responses to “How to use soft PWM in RPi.GPIO 0.5.2a pt 2 – led dimming and motor speed control”

  1. What language and program would you recommend for precise control of motors and reasonably accurate control of servos?? (robotic project coming up hopefully :heh: ) well if all goes to plan anyway :idk: because from reading though it sounds like pwm through RPI.GPIO isn’t accurate enough for that kind of application.
    Also from experience of boards/chips which from your point of view are the best for this kind of multiple motor use because if im honest we are starting from very little knowledge and have quite high aims which means unfortuanately we need lots of advice :L

  2. [...] How to drive leds and motors with PWM & RPi.GPIO on the Raspberry Pi: In part 1 of this series, we looked at the basic commands for using software pulse-width modulation (PWM) in RPi.GPIO 0.5.2a and higher. In this article we’ll get a bit more hands-on and into some practical applications for it. It’s all very well being able to make nice square-wave pulses on an oscilloscope, but what’s it actually useful for? [...]

  3. I’m a bit concerned about the non-real-time and non-timing-critical aspects. What kinds of inaccuracies are you talking about here?
    I’m planning on building a robot with lego motors and parts, with a pi brain. Lego gear chains have plenty of slop in them already. Would python timing issues be noticable on top of that when the pi cpu is heavily loaded or python garbage collection kicks in?
    I’m not out for incredible accuracy here, but I’m hoping my motors will respond within a few hundred miliseconds

    • I think we’re talking milli to microseconds. I’ve noticed the occasional jitter on a brushed motor. You should be OK with a small robot, but I wouldn’t want to use it on a CNC router or something big, powerful and accurate.

      I managed a native Python motor driver in the Gertboard software that could cope with 3000 Hz pulses. It did jitter a bit, but it was usable.

      With this PWM function it’s noticably better than mine. It’s just not quite there for critical things like servos which require precisely timed pulses.

  4. Thank you for this tutorial. It’s very clear and helpful. I will suggest that a more common cycling loop pattern would be:

    lo, hi, step = 0, 10, 1
    while True:
    for i in range(min(lo, hi), max(lo, hi), abs(step)):
    #white.ChangeDutyCycle(lo)
    #red.ChangeDutyCycle(hi)
    print (lo, hi)
    lo += step
    hi -= step
    step *= -1

    and if you hate the use of min, max, and abs you can always duplicate the variable at the beginning:

    lo, hi, step = 0, 10, 1
    rlo, rhi, rstep = lo, hi, step
    while True:
    for i in range(rlo, rhi, rstep):
    #white.ChangeDutyCycle(lo)
    #red.ChangeDutyCycle(hi)
    print (lo, hi)
    lo += step
    hi -= step
    step *= -1

  5. In the diagram with the motor there’s a red wire just to the left/above the motor that doesn’t seem to actually do anything. It’s connected to the Positive power line on one end of the wire but the other end does not appear to actually connect to anything.

    • It’s connecting the positive rail to the motor’s green wire. It’s redundant, but the way the Fritzing diagram is laid out it would have looked crap to have the green wire bent backwards straight into the positive rail. (Actually it looks a bit crap anyway, but I’m not going to change it now, just for that).

  6. […] Taken from RasPi.tv. Use a green light instead of that white color, so that our “Red Light, Green Light” game makes sense. […]

  7. Hi, how can I change the phase of the pwm? I’m trying to make 4 leds blink at different times with the same frequency but different phase, how can I manage that? I’ve tried to make a delay before starting each pwm object, but they tend to sync at the first blink, what can I do to change each phase?
    Thx!

    • If you’re trying to make LEDs blink, rather than dim them, wouldn’t you be better off coding it manually, rather than using PWM, which is designed for fast blinking (faster than you can see)?

      • Thank you for your reply.
        Well, the actual purpose of the code is to make pulses for a stepper motor, which uses 4 outputs. I’m using LEDs so I can see the actual outputs of the code before using the stepper motor.

        I’m using webiopi to control de RPi from a webpage. When a button gets pressed, it calls a function from another script that contains the pwm setup in the 4 outputs at 1 Hz and a duty cycle of 25%.

        Here’s a bit of my code:

        b1 = GPIO.PWM(18, 1)
        b2 = GPIO.PWM(22, 1)
        b3 = GPIO.PWM(23, 1)
        b4 = GPIO.PWM(27, 1)

        b1.start(25)
        time.sleep(0.25)
        b2.start(25)
        time.sleep(0.25)
        b3.start(25)
        time.sleep(0.25)
        b4.start(25)

        Notice the delays I made so the pwm outputs alternate, the delays work, but the pwm outputs sync just after the first blink, which is bad for me :(.

        Thing is… I cannot code the pins manually on/off in a loop because the function is called only once (when the button is pressed), when the button is not pressed and the function HAS ENDED, my main script calls a function that stops the pwm outputs turning them off.

        I cannot put the function in the main script because webiopi does not support software pwm with low frequencies, so i’m forced to use another script.