Want to learn how to control the GPIO ) pins of a Raspberry Pi with Python? Well, you're in the right place. (general-purpose input/output In this project we'll just be turning on a few LEDs, but the main concepts covered here could easily be extended to trigger more complex events. This is a fun physical computing project that would be great for teaching kids the basics of Python programming while giving them the satisfaction of seeing their code change things in the real world! Project Overview This tutorial will cover: Installing Raspbian on a Raspberry Pi Configuring remote access to a Raspberry Pi via SSH Setting up a Python development environment on a Raspberry Pi Setting up and connecting a breadboard circuit with LEDs to the Pi Writing a simple Python program to control the GPIO pins of the Pi to turn LEDs on/off For this project you will need: Raspberry Pi micro-SD card 9 - LEDs (preferably multicolored: 3xR, 1xG, 2xB, 3xY in this example) 9 - 1k resistors (anything over 100Ω should be fine) breadboard 10 - GPIO pin connecting cables There are many kits available on Amazon for under $20. If you already have access (either remotely, or via a desktop environment) to a Raspberry Pi, you can skip ahead to the section. If not, read on. Setting Up Your Development Environment Note: terminal commands may be prefixed by a localmachine:~$ or pi:~$ prompt in parts where it may not be clear which machine we are logged into. These prefixes are not meant to be typed and only serve as a reminder of which machine we should be logged into at that point. Installing Raspbian The first step is to set up a Raspberry Pi with a headless version of Raspbian and enable SSH access. If you want to attach a monitor to your Pi and develop locally on it, download the desktop version. Download your choice of Raspbian and unzip it. here The next step is to write the Raspbian image onto a micro-SD card. I'm going to do this from the Linux command-line, but you can use a GUI disk writer like if you'd like. Etcher Note : dd is often not -so-jokingly referred to as ‘Disk Destroyer’ because if you type even one wrong character in a dd command, you can instantly and permanently wipe out an entire drive of valuable data. Consider yourself warned! Before you plug in the SD card to your computer open a terminal and run: df -h -x squashfs -x tmpfs You should see output similar to this: Filesystem Size Used Avail Use% Mounted on udev 16G 0 16G 0% /dev /dev/sda1 440G 364G 53G 88% / /dev/sdc 1.8T 548G 1.2T 32% /media/backups This is listing the file systems on our machine. Now plug in the SD card into your laptop or desktop and run it again. df -h -x squashfs -x tmpfs You should see a new device: Filesystem Size Used Avail Use% Mounted on udev 16G 0 16G 0% /dev /dev/sda1 440G 364G 53G 88% / /dev/sdc 1.8T 548G 1.2T 32% /media/backups /dev/sdf 16G 0 6GB 6% /media/sandisk-SD This is the device name of our SD card. In this case . sdf Now run , specifying the path to your extracted Raspbian image after the flag: dd if= dd =/path/to/2019-07-10-raspbian-buster-lite.img of=/dev/sdf bs=1m status=progress if Wait for dd to finish and you will have a Raspbian image written to your SD card with a and partition. boot rootfs Enabling Remote Login to the Pi with SSH Now that we have Raspbian on our micro-SD card, before we install it in the Raspberry Pi, we need to add an empty file named to the partition of the SD card. This will enable us to connect to the Raspberry Pi via SSH from a computer on our network. ssh (without any extension) boot If you are using a desktop version of Raspbian and plan to develop directly on the Pi you can skip to the next section ( Setting Up Your Development Environment ). I will assume you are using a Mac or Linux machine. If you are using Windows, see page for using SSH from Windows. this Most Linux distros should already have an SSH client like OpenSSH installed and enabled. You can check by typing in a terminal. You should see output similar to the following: ssh username@host:~$ ssh usage: ssh [-1246AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec] [-D [bind_address:]port] [-E log_file] [-e escape_char] [-F configfile] [-I pkcs11] [-i identity_file] [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec] [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address] [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]] [user@]hostname [ ] command If the command is not recognized, it should give you some suggestions on how to install it. On a Debian based Linux distribution, like Ubuntu or Raspbian, you can use (Advanced Package Tool) to install OpenSSH: APT sudo apt install openssh-server Enable the ssh service: sudo systemctl ssh enable Start the ssh service: sudo systemctl start ssh Now that we've verified we have an SSH client running on our local machine, put the SD card in the Raspberry Pi, connect it your network and power it up. It's easier to just use an ethernet cable, but if you will be connecting your Pi to the network via Wi-Fi, see guide. this We'll need to find the private IP address of our Pi. This can be done by pinging the default hostname of the Pi which is . raspberrypi ping raspberrypi And you should see output similar to: PING raspberrypi.isp.provider.net ( ) ( ) bytes data. bytes raspberrypi.isp.provider.net ( ): icmp_seq= ttl= time= ms bytes raspberrypi.isp.provider.net ( ): icmp_seq= ttl= time= ms bytes raspberrypi.isp.provider.net ( ): icmp_seq= ttl= time= ms bytes raspberrypi.isp.provider.net ( ): icmp_seq= ttl= time= ms bytes raspberrypi.isp.provider.net ( ): icmp_seq= ttl= time= ms bytes raspberrypi.isp.provider.net ( ): icmp_seq= ttl= time= ms bytes raspberrypi.isp.provider.net ( ): icmp_seq= ttl= time= ms ^C --- raspberrypi.isp.provider.net ping statistics --- packets transmitted, received, % packet loss, time ms rtt min/avg/max/mdev = / / / ms 10.0 .0 .14 56 84 of 64 from 10.0 .0 .14 1 64 0.407 64 from 10.0 .0 .14 2 64 0.373 64 from 10.0 .0 .14 3 64 0.350 64 from 10.0 .0 .14 4 64 0.474 64 from 10.0 .0 .14 5 64 0.359 64 from 10.0 .0 .14 6 64 0.512 64 from 10.0 .0 .14 7 64 0.389 7 7 0 6125 0.350 0.409 0.512 0.057 If the hostname can't be resolved by your machine, you can find out your local network's information and scan it for connected devices using and . You check out an article I wrote on how to do that . raspberrypi ifconfig nmap here From the output of the command we can see that the IP address of the Raspberry Pi is . So let's try logging into the Pi as the default user from our local machine using ssh: ping 10.0.0.14 pi ssh pi@raspberrypi Or using the IP address we discovered above: ssh pi@ 10.0 .0 .14 You should be greeted with something like this if this is the first time connecting to your Pi: The authenticity host ECDSA fingerprint SHA256:qeHIC464zGhEecXmkEiqADemCp96/HnJ76ZJOF8sc8Y. Are you sure you want connecting (yes/no)? of 'raspberrypi (10.0.0.14)' can't be established. key is to continue Type and you'll be greeted with a prompt asking for your password. Use the default password . y raspberry After entering the password you will be greeted with some system information about your Raspberry Pi and will be prompted to change the default password. So let's do that by typing : passwd pi@raspberrypi:~$ passwd After setting up a new password, let's logout and try logging back in with our new password: localmachine:~$ ssh pi@raspberrypi Okay, we’ve verified that everything’s working, but it’d be nice if we didn’t have to type in the username and hostname every time we wanted to log into our Pi). I definitely have a hard time remembering IP addresses, usernames, and hostnames. (or worse; the IP address) Let's log back out of our Pi and edit the ssh file on our local machine so we can log in using one easy to remember command. config You should have a directory in the home directory of your local machine. If not, you can create one. Let's change into that directory: .ssh localmachine:~$ ~/.ssh cd If there's not a file called in the directory, create one and add the following lines: config .ssh Host pi Hostname raspberrypi User pi # or ip addr e.g. 10.0.0.14 Save the file. You should now be able to log into the Raspberry Pi by typing : ssh pi localmachine:~$ ssh pi Okay, so that's easier than typing in the username and hostname/IP address every time we log in. However, we still need to type in a password each time we connect to our Pi which can get a little annoying after a while. To enable remote access to our Raspberry Pi without having to type in a password, let's now set up some SSH keys. Make sure you're in the directory of your local machine and type: .ssh ssh-keygen -f pi You will be prompted with: Generating public/private rsa key pair. Enter passphrase (empty no passphrase): Enter same passphrase again: for Leave the passphrase empty. This password is for added security. The whole point of setting up these keys in this case is so we have to use a password to login to our Raspberry Pi. Just hit to leave it blank. don't enter Don't worry using an SSH key without a passphrase is still much more secure than using only a password without an SSH key provided you have disabled root and password login. With password and root logins disabled, an attacker would need access to the private key on your local machine to gain access to the Raspberry Pi. If you wish to disable root login and password logins after we generate our SSH keys, see this article on how to do that. The will now generate a pair of private and public keys. ssh-agent Your identification has been saved in pi. Your public key has been saved in pi.pub. The key fingerprint is: SHA256:AK1zJarZrhBYsU3ertvfsa448wI+ZVjrSwgcs6BJjVfrt/M user The key's randomart image is: +---[RSA 2048]----+ | | | | | | | | | | | | | | | | | | +----[SHA256]-----+ @localmachine . +o. . . = +.o.+ o +o=o+ + .. .+..+ * =. o +oo S +o.. .o . +oo+ . . .E=+ = . o.oo .. ..o. The contents of your directory on your local machine should now look something like: .ssh authorized_keys known_hosts pi.pub pi config In the last command, generated a private and public key pair. We'll now need to copy the public key from our local machine to the file on our Raspberry Pi. You can do this manually by copying the text contents of and pasting it into the Pi's file. However, the , installed with , has a tool that makes this a breeze. ssh-keygen pi.pub authorized_keys pi.pub authorized_keys ssh-agent OpenSSH-server Just type the following command in a terminal from within the directory on your local machine: .ssh ssh- - -i .pub copy id pi pi You'll see the following output: /usr/bin/ssh- -id: : Source key(s) be installed: "pi.pub" /usr/bin/ssh- -id: : attempting the key(s), that are already installed /usr/bin/ssh- -id: : key(s) remain be installed Number key(s) added: Now try logging the machine, : "ssh 'pi'" make sure that the key(s) you wanted were added. copy INFO of to copy INFO to log in with new to filter out any copy INFO 1 to -- if you are prompted now it is to install the new keys of 1 into with and check to only So, let's try logging in: ssh pi Right on. We can now quickly and easily log into our Raspberry Pi with one easy-to-remember command. We can also transfer files between our local machine and the Raspberry Pi in the same manner using : sftp localmachine:~$ sftp Connected . sftp> source_code.py pi to pi put See video if there's any confusion. It covers these steps in detail. this Setting Up Your Development Environment Now that we have SSH access to our Raspberry Pi, it's time to set up our Python development environment. Let's start by logging into our Pi and updating our packages. If you're logging in remotely via SSH: localmachine:~ ssh $ pi or, if you're developing directly on the Raspberry Pi with a desktop version of Raspbian with a monitor, just open a terminal. Python 3 should already be installed on the Raspberry Pi, but you can check by running: pytho -V n3 If it's not installed, run: sudo apt python3 install We’re going to want to use the Python package manager to install Python libraries that aren't part of the standard library. It can be installed with: pip sudo apt python3-pip install I have a public GitHub repository set up with all the code examples and schematics used in this tutorial. You can find it . Let's install so you can clone the repository to your machine if you'd like. here git sudo apt install git You can run git clone https://github.com/avcourt/restful-pi to copy the contents of my repository to your machine. It contains the schematics of the LED circuit above in the img/ directory. It also contains the source code of the Python program pin_controller.py that we will begin to write shortly. The repository is also home to a REST API back-end written using the Python micro-web framework Flask. This Flask app (called app.py) allows you to control the pins of a Raspberry Pi over the internet by making HTTP requests to a Flask server running on a Raspberry Pi but will not be covered in this article. I prefer to use for text editing remotely, but pick whichever text editor you prefer: vim sudo apt vim install We’ll also need a Python library to enable us to control the GPIO pins of the Raspberry Pi using Python. Generally, it's good practice to use a Python virtual environment to handle all your Python dependencies, but for simplicity, we're just going install all of our packages globally. I would suggest looking up a tool like if virtual environments are new to you. Pipenv Let's use to install the Python library : pip RPi.GPIO pip3 RPi.GPIO install That should be all the software we need to start writing a Python program to control the pins of our Raspberry Pi. Let's move on to setting up our LED circuit using the breadboard. Breadboard Set Up The code example that will follow this section uses the following configuration for pins and LEDs: { : , : ,} { : , : }, { : , : }, { : , : }, { : , : }, { : , : }, { : , : }, { : , : }, { : , : } "pin_num" 23 "color" "red" "pin_num" 24 "color" "yellow" "pin_num" 25 "color" "blue" "pin_num" 22 "color" "red" "pin_num" 12 "color" "yellow" "pin_num" 16 "color" "blue" "pin_num" 20 "color" "red" "pin_num" 21 "color" "green" "pin_num" 13 "color" "yellow" These pin numbers refer to the GPIO pin numbering, not the generic pin numbering: If you set up your breadboard using the pin configuration shown above, your circuit should look like this: Python Program to Turn LEDs On and Off Now that our breadboard circuit is set up, let's start writing some Python code to light up some of these LEDs! We'll start by logging into our Raspberry Pi and making a directory to put our LED Python program in: :~ mkdir led_pgms :~ cd led_pgms pi $ pi $ Next, open your favorite text editor and create a file called . pin_controller.py We'll start by importing and creating a list of pins using the configuration shown up above in the schematic: RPi.GPIO RPi.GPIO GPIO pins = [{ : , : }, { : , : }, { : , : }, { : , : }, { : , : }, { : , : }, { : , : }, { : , : }, { : , : }] import as 'pin_num' 23 'color' 'red' 'pin_num' 24 'color' 'yellow' 'pin_num' 25 'color' 'blue' 'pin_num' 22 'color' 'red' 'pin_num' 12 'color' 'yellow' 'pin_num' 16 'color' 'blue' 'pin_num' 20 'color' 'red' 'pin_num' 21 'color' 'green' 'pin_num' 13 'color' 'yellow' If your circuit varies when compared to the schematic up above, either in LED color or the GPIO pin numbers that are connected to the breadboard, this pins list is the place to change it. Next, let's tell to use the GPIO pin numbering format, not the generic pin numbering: RPi.GPIO GPIO.setmode(GPIO.BCM) # use GPIO numbering, not generic And finally let's set up all our pins as output power pins and initialize their state to (off): LOW pin pins: GPIO.setup(pin[ ], GPIO.OUT, initial=GPIO.LOW) for in 'pin_num' The above code snippet loops through our list of pins and gets the integer pin value stored in each pin dictionary and sets it as an output pin so we can turn the power to that GPIO pin on or off. (a 'pin' being a Python dictionary containing 2 keys: the pin number and led color associated with that GPIO pin), So far our program should like this: RPi.GPIO GPIO pins = [{ : , : }, { : , : }, { : , : }, { : , : }, { : , : }, { : , : }, { : , : }, { : , : }, { : , : }] GPIO.setmode(GPIO.BCM) pin pins: GPIO.setup(pin[ ], GPIO.OUT, initial=GPIO.LOW) import as 'pin_num' 23 'color' 'red' 'pin_num' 24 'color' 'yellow' 'pin_num' 25 'color' 'blue' 'pin_num' 22 'color' 'red' 'pin_num' 12 'color' 'yellow' 'pin_num' 16 'color' 'blue' 'pin_num' 20 'color' 'red' 'pin_num' 21 'color' 'green' 'pin_num' 13 'color' 'yellow' # use GPIO numbering, not generic # setup all pins based on above configuration for in 'pin_num' Now that we have all the set up taken care of, let's write a function that will turn a specific color of LEDs on or off: pin_nums = [pin[ ] pin pins pin[ ] == color] pin_num pin_nums: state == : GPIO.output(pin_num, GPIO.HIGH) state == : GPIO.output(pin_num, GPIO.LOW) : def toggle_color (color: str, state: str) 'pin_num' for in if 'color' for in if 'on' elif 'off' The line: pin_nums = [pin[ ] pin pins pin[ ] == color] 'pin_num' for in if 'color' uses a Python list comprehension to create a list of the GPIO pin numbers that power an LED of the function parameter . So if we call , this statement will loop through the list and collect the pin numbers that are associated with a red LED. In this case if , = . color toggle_color('red, 'on') pins color='red' pin_nums [23, 22, 20] List comprehensions are a concise way of creating lists; a bit of syntactic sugar in Python.They are considered a "Pythonic" way of creating lists while programming in Python. The above code snippet is logically equivalent to: pin_nums = [] pin pins: pin[ ] == color: pin_nums.append(pin[ ]) for in if 'color' 'pin_num' Let's save this file as and open an interactive Python shell while in the same directory as to test out our newly created function: pin_controller.py pin_controller.py toggle_color :~ python3 pi $ You will be greeted with an interactive command prompt: Python (default, Apr , : : ) [GCC ] on linux Type , , more information. 3.7 .3 3 2019 05 39 12 8.2 .0 "help" "copyright" "credits" or "license" for >>> The Python Interactive Interpreter allows us to run commands and have them executed immediately. Very useful for experimenting and debugging. Let's start by importing the code we just wrote: pin_controller >>> import This will execute each line of the code contained in . pin_controller.py We can now test out the function we just wrote: toggle_color() pin_controller.toggle_color( , ) >>> 'red' 'on' Hopefully, if we set our breadboard circuit up correctly, the 3 red LEDs on the board should have turned on. Lets turn them off: pin_controller.toggle_color( , ) >>> 'red' 'off' Cool! Let's exit out of the Python shell by typing and write a couple more functions. exit() Open up again and let's add two functions that turn all the pins on or off: pin_controller.py pin pins: GPIO.output(pin[ ], GPIO.HIGH) pin pins: GPIO.output(pin[ ], GPIO.LOW) : def all_on () for in 'pin_num' : def all_off () for in 'pin_num' Save the file and test the functions out again in a new interactive Python shell: python3 Python (default, Apr , : : ) [GCC ] on linux Type , , more information. pin_controller >>> 3.7 .3 3 2019 05 39 12 8.2 .0 "help" "copyright" "credits" or "license" for >>> import Turn all LEDs on: pin_controller.all_on() >>> Right on! All the LEDs just lit up at once! Let's turn them all off now: pin_controller.all_off() >>> Don't worry if you get a warning about a GPIO channel already being in use. This warning is being shown because we didn't set the output state to off for all the active GPIO pins before our program terminated. You can add the following line to your program to suppress these warning if you'd like: GPIO.setmode(GPIO.BCM) GPIO.setwarnings( ) # use GPIO numbering, not generic ## add the following line vvv False # suppress pin channel warnings Okay so that's kinda cool, but we can probably write a more interesting function. Let's write a function that uses the and functions we just wrote to make a strobe effect. We'll want to use the function so we'll need to import from the Python standard library. Add the following to the top of : all_on() all_off() time.sleep() time pin_controller.py time import Now we'll write a function that turns all the LEDs on and then off in a regular period called . Think about how you might write this function yourself before looking below: strobe_reg() : all_on() time.sleep(period) all_off() time.sleep(period) : def strobe_reg (period= ) 0.5 while True This function simply creates an infinite loop and turns all the LEDs on or off every half a second. This function also uses a default argument called . If is called without an argument, the period value used in the function defaults to 0.5 seconds, however, if an argument is provided to the function that value will be used as the period: period strobe_reg() sleep() e.g. strobe_reg(0.1) would have a faster strobe effect Let's add the function to and create one more similar function. This function will have some randomization to it provided by the module in the Python standard library. Add to the top of and then add this function below strobe_reg() pin_controller.py random import random pin_controller.py strobe_reg(): : all_on() time.sleep(random.uniform(min_time, max_time)) all_off() time.sleep(random.uniform(min_time, max_time)) : def strobe_rand (min_time= , max_time= ) 0 1.2 while True uses default values again to provide upper and lower ranges to the method. return a random float in the range [a,b] for a more "random" strobe effect. strobe_rand() random.uniform() random.uniform(a,b) Let's save and try out the new functions again in the Python shell: pin_controller.py python3 Python (default, Apr , : : ) [GCC ] on linux Type , , more information. pin_controller pin_controller.strobe_reg() 3.7 .3 3 2019 05 39 12 8.2 .0 "help" "copyright" "credits" or "license" for >>> import >>> Note: since strobe_reg() relies on an infinite loop, you'll have to type ctrl+c to kill the last command and return control to the Python shell. This will apply to all of our functions that use an infinite loop. Hit to stop : ctrl+c strobe_reg() ^CTraceback (most recent call last): File , line , <module> File , line , strobe_reg time.sleep(period) KeyboardInterrupt "<stdin>" 1 in "/home/pi/restful-pi/pin_controller.py" 63 in and let's try with a shorter period: strobe_reg() pin_controller.strobe_reg( ) >>> 0.1 You can start to see how you can get quite creative with your own "lightshow" functions. I'll give you 3 more functions to try out, but then I want you to create your own. Add the following code snippet to the bottom of : pin_controller.py : pin pins: GPIO.output(pin[ ], GPIO.HIGH) time.sleep(period) pin reversed(pins): GPIO.output(pin[ ], GPIO.LOW) time.sleep(period) : period = random.uniform(min_time, max_time) pin pins: GPIO.output(pin[ ], GPIO.HIGH) time.sleep(period) period = random.uniform(min_time, max_time) pin reversed(pins): GPIO.output(pin[ ], GPIO.LOW) time.sleep(period) : pin pins: GPIO.output(pin[ ], GPIO.HIGH) time.sleep(random.uniform(min_time, max_time)) pin reversed(pins): GPIO.output(pin[ ], GPIO.LOW) time.sleep(random.uniform(min_time, max_time)) : def wave_reg (period= ) 0.1 while True for in 'pin_num' for in 'pin_num' : def wave_rand (min_time= , max_time= ) 0 0.4 while True for in 'pin_num' for in 'pin_num' : def wave_rand_ex (min_time= , max_time= ) 0 0.4 while True for in 'pin_num' for in 'pin_num' These wave functions use the method to reverse the pin list and create a sort of up-and-down wave effect. If you were to remove one of the loops it would simply turn on the lights in one direction only. Try it out! reversed() If you were using a large list it would probably be a good idea to create the reversed pin list before the while loop so we'd only have to call the reversed() method once, but in this program, the performance increase is probably negligible. Your program should now look something like: pin_controller.py RPi.GPIO GPIO random time pins = [{ : , : }, { : , : }, { : , : }, { : , : }, { : , : }, { : , : }, { : , : }, { : , : }, { : , : }] GPIO.setmode(GPIO.BCM) GPIO.setwarnings( ) pin pins: GPIO.setup(pin[ ], GPIO.OUT, initial=GPIO.LOW) pin_nums = [pin[ ] pin pins pin[ ] == color] pin_num pin_nums: state == : GPIO.output(pin_num, GPIO.HIGH) state == : GPIO.output(pin_num, GPIO.LOW) toggle_color(color, ) toggle_color(color, ) pin pins: GPIO.output(pin[ ], GPIO.HIGH) pin pins: GPIO.output(pin[ ], GPIO.LOW) : all_on() time.sleep(period) all_off() time.sleep(period) : all_on() time.sleep(random.uniform(min_time, max_time)) all_off() time.sleep(random.uniform(min_time, max_time)) : pin pins: GPIO.output(pin[ ], GPIO.HIGH) time.sleep(period) pin reversed(pins): GPIO.output(pin[ ], GPIO.LOW) time.sleep(period) : period = random.uniform(min_time, max_time) pin pins: GPIO.output(pin[ ], GPIO.HIGH) time.sleep(period) period = random.uniform(min_time, max_time) pin reversed(pins): GPIO.output(pin[ ], GPIO.LOW) time.sleep(period) : pin pins: GPIO.output(pin[ ], GPIO.HIGH) time.sleep(random.uniform(min_time, max_time)) pin reversed(pins): GPIO.output(pin[ ], GPIO.LOW) time.sleep(random.uniform(min_time, max_time)) import as import import 'pin_num' 23 'color' 'red' 'pin_num' 24 'color' 'yellow' 'pin_num' 25 'color' 'blue' 'pin_num' 22 'color' 'red' 'pin_num' 12 'color' 'yellow' 'pin_num' 16 'color' 'blue' 'pin_num' 20 'color' 'red' 'pin_num' 21 'color' 'green' 'pin_num' 13 'color' 'yellow' # use GPIO numbering, not generic False # setup all pins based on above configuration for in 'pin_num' : def toggle_color (color: str, state: str) 'pin_num' for in if 'color' for in if 'on' elif 'off' : def color_on (color: str) 'on' : def color_off (color: str) 'off' : def all_on () for in 'pin_num' : def all_off () for in 'pin_num' : def strobe_reg (period= ) 0.5 while True : def strobe_rand (min_time= , max_time= ) 0 1.2 while True : def wave_reg (period= ) 0.1 while True for in 'pin_num' for in 'pin_num' : def wave_rand (min_time= , max_time= ) 0 0.4 while True for in 'pin_num' for in 'pin_num' : def wave_rand_ex (min_time= , max_time= ) 0 0.4 while True for in 'pin_num' for in 'pin_num' Experiment in the Python Interactive Interpreter by using different values for the functions that take timing parameters or try changing the source code a bit and see what happens. After that, write your own "lightshow" functions. It'd be great if you shared them down in the comments below! Hope you learned something and had some fun while you were at it! a short video going over the the initial set up and testing some of these functions in the Python shell. All the code can also be found on my GitHub . Here's profile Thanks for reading and don't forget to leave a comment with any lightshow functions you're proud of! You can follow me on for similar content. Happy coding! Twitter @avcourt