Next, we will learn an important concept – PWM, or Pulse Width Modulation. It is used to switch the LED between on and off fast. Remember the test before we add a time module? The LED blinks too fast, so it seems to be on all the time. That is the principle of the PWM. So what would happen if we change the duration of the LED on and off? What if we make it bright for shorter time, and off for longer?
Let’s give it a try!
Keep the wiring the same, and save a copy of the code in last chapter as pwm_led.py. If you have a display, click File -> Save as in the Python IDLE or just press Ctrl + Shift + S to save a copy.
If you don’t have a screen, you can use the cp command:
|cp blink_led.py pwm_led.py|
Then edit the file:
Now we need to change delay into two variables: on_delay and off_delay, and assign 0.005 to both:
import RPi.GPIO as GPIO import time led_pin = 17 on_delay = 0.005 off_delay = 0.005 GPIO.setmode(GPIO.BCM) GPIO.setup(led_pin, GPIO.OUT) while True: GPIO.output(led_pin, GPIO.LOW) # LED ON time.sleep(on_delay) # Sleep "on_delay" second GPIO.output(led_pin, GPIO.HIGH) # LED OFF time.sleep(off_delay) # Sleep "off_delay" second
Remember to change the two delay in the bottom to on_delay and off_delay too.
Save and run the code. The LED would not blink, right? As the frame perceiving time for human eye is about 0.01 seconds (about 60 frames per second), through setting the two variables to 0.005s, a cycle will be 0.01s (one on + one off), so it seems the LED would not blink – actually it’s that our eyes cannot recognize the frame change.
Let’s write a simple formula to calculate the off_delay time:
|off_delay = 0.01-on_delay|
Thus once we change the value of on_delay, that of off_delay will be changed accordingly, keeping a whole cycle duration at 0.01s.
For example, modify on_delay to 0.001s, then off-delay will be 0.01-0.001=0.009 s. So that’s what we want it to be: keep the LED on for a shorter time and off longer:
import RPi.GPIO as GPIO import time led_pin = 17 on_delay = 0.001 off_delay = 0.01 - on_delay GPIO.setmode(GPIO.BCM) GPIO.setup(led_pin, GPIO.OUT) while True: GPIO.output(led_pin, GPIO.LOW) # LED ON time.sleep(on_delay) # Sleep "on_delay" second GPIO.output(led_pin, GPIO.HIGH) # LED OFF time.sleep(off_delay) # Sleep "off_delay" second
Save the code and run it. The LED becomes dimmer, right? Press Ctrl + C to stop it, and change on_delay to 0.009, meaning off_delay will be 0.001 accordingly. Then run it again.
Amazing! The LED becomes brighter now! Have you got it? First, set the cycle duration beyond recognition limitation of the eye, and then adjust the brightness of the LED by changing the pulsing width, i.e., the time of LED on and off, so that is how the PWM works.
The previous experiment is just to help you learn the PWM principle. We would not write such a code usually – too tedious. There is a built-in function in the RPi.GPIO – the GPIO.PWM.
Modify the code like this:
import RPi.GPIO as GPIO import time led_pin = 17 on_delay = 0.5 # Set on_delay to 0.5 which we can see the blink off_delay = on_delay # Set off_delay equals on_delay freq = 100 # Frequency in Hz GPIO.setmode(GPIO.BCM) GPIO.setup(led_pin, GPIO.OUT) p = GPIO.PWM(led_pin, freq) # Set up p as PWM at pin "led_pin" and frequency "freq" p.start(0) # Start p at duty cycle 0 while True: p.ChangeDutyCycle(50) # Change Duty Cycle(brightness) to 50, ranges: 0~100 time.sleep(on_delay) p.ChangeDutyCycle(50) # Change Duty Cycle(brightness) to 10, ranges: 0~100 time.sleep(off_delay)
Those lines with the # mark are modified or added.
Here we add a variable freq = 100, which is the frequency, and its unit is Hz. In GPIO.PWM, we use the frequency in the code. The formula to convert cycle to frequency:
Frequency (Hz) = 1(s) / on_off_cycle(s)
Thus the frequency should be set higher than 1/0.01 (on_off_cycle), that is, higher than 100Hz.
But there still has a problem: the LED controlling is reversed – it is off when its cathode is connected to a high voltage at the I/O pin, while it’s on when connected to low level. Therefore, the LED will be brightest if we set the PWM value to 0, and darkest if it’s 100, which is absolutely contrary to common sense. Let’s write a simple function to solve this problem: the LED will be brightest if the value is 100 rather than 0, and vice versa, thus the code makes sense.
This is a simple example of function statement, define (def) a function named set_led_value, and substitute the parameter with value. This function will run the ChangeDutyCycle, and use 100-value to reverse the old value, so that the code will be easy to understand and make more sense. If you want to change the brightness of the LED, just use set_led_value.
Add the two lines above, and modify the function in while:
import RPi.GPIO as GPIO import time led_pin = 17 on_delay = 0.5 off_delay = on_delay freq = 100 GPIO.setmode(GPIO.BCM) GPIO.setup(led_pin, GPIO.OUT) p = GPIO.PWM(led_pin, freq) p.start(0) def set_led_value(value): # Define a function p.ChangeDutyCycle(100-value) while True: set_led_value(50) # use our function to set led value to 50 time.sleep(on_delay) set_led_value(10) # use our function to set led value to 10 time.sleep(off_delay)
Save the code and run it, and the LED will blink between two levels of brightness: 50 and 10 (brighter – dimmer). You can modify the brightness value to try and check the change.
So, what shall we do next?