Building a Larson Scanner
Introduction
The Larson Scanner is one of those timeless electronics projects — the hypnotic red sweep made famous by the Cylons in Battlestar Galactica and KITT in Knight Rider. Both were created by Glen A. Larson, and both inspired generations of makers to recreate that iconic effect.
This build brings the classic scanner into the Raspberry Pi era using a custom PCB, 24 LEDs, and a dash of Charlieplexing magic.
Instead of dedicating one GPIO pin per LED, we’ll use Charlieplexing, a clever wiring technique that allows control of n2−n LEDs with only n pins. With 6 GPIO pins, we can drive up to 30 LEDs — more than enough for our 24‑LED scanner.
Project Overview
PCB Layout
I wanted to make it as compact as possible while laying out the LED lights in a straight line, after a few failed attempts I came up with the solution below. The board was designed in Sprint‑Layout and arranges all 24 LEDs in a clean, straight line for that unmistakable scanner look.
Fabrication
Fortunately for me, my brother is in to robotics and had recently built himself a PCB photo etching box out of an old scanner, I was thus able to get the PCB made fairly easily. Because Charlieplexing relies on precise pin‑to‑pin relationships, careful soldering is essential.
Charlieplexing Setup
Charlieplexing lets you control up to n2−n LEDs using only n GPIO pins. With 6 pins, the Pi can theoretically drive 30 LEDs — plenty for this project’s 24‑LED array.
Pins used: GPIO 13, 16, 19, 20, 21, 26
Each LED is defined by a pair of pins: one set HIGH, one set LOW, and the rest left as inputs.
Software Implementation
The scanner is driven by a Python script using the RPi.GPIO library.
sudo apt-get update
sudo apt-get install rpi.gpio
Key Concepts
Pin Resetting: Before lighting each LED, all pins are set to input mode to prevent ghosting.
Lighting an LED: One pin goes HIGH, another LOW, the rest stay as inputs.
Sweep Effect: LEDs are lit in sequence, then reversed, creating the classic back‑and‑forth motion.
Core Script
The page includes the full Python script that:
Builds the Charlieplex LED matrix
Defines the sweep order
Supports an optional repeat count via
-lMonitors GPIO pin 5 for an external stop signal
Cleans up GPIO state on exit
Parts
Pi Zero 2W Pi Zero 2 W – Raspberry Pi
ATXRaspi ATXRaspi | LowPowerLab
Switch Momentary push button
Custom LED PCB
import sys, getopt
# Import the module that runs the GPIO pins
import RPi.GPIO as gpio
# Import the sleep function for pausing
from time import sleep
# set the GPIO pins to BOARD mode
gpio.setmode(gpio.BCM)
#If an argument is passed then set the repeat counter
repeatcount=0
myopts, args = getopt.getopt(sys.argv[1:],"l:")
for o, a in myopts:
if o == '-l':
repeatcount=int(a)
# This function lights a specific LED. The led variable input is a 2 item
# list, each item representing a pin
def lightLED(led):
#First clear the pins, by setting them all to input
for pin in charliePins:
gpio.setup(pin,gpio.IN)
#Now set up the first pin for HIGH OUTPUT
gpio.setup(led[0],gpio.OUT)
gpio.output(led[0],gpio.HIGH)
#Now set up the second pin for LOW OUTPUT
gpio.setup(led[1],gpio.OUT)
gpio.output(led[1],gpio.LOW)
# Define the array of pins used as leads
charliePins=[13,16,19,20,21,26]
# Define the order for triggering the LEDs
charlieOrder=[1,0,3,2,5,4,7,6,9,8,11,10,13,12,15,14,17,16,19,18,21,20,23,22]
# Build the array of LEDs by cycling through the pins and creating the pairs.
# It has the disadvantage of not making them in order for larger sets of
# pairs, but is easier to maintain, IMO.
charlieLEDS=[]
for i in range(0,len(charliePins)-1):
for j in range(i+1,len(charliePins)):
charlieLEDS.append([charliePins[i],charliePins[j]])
charlieLEDS.append([charliePins[j],charliePins[i]])
#Run the code over and over and over again
try:
counter = 0
while 1:
if repeatcount > 0:
counter=counter+1
for led in charlieOrder:
lightLED(charlieLEDS[led])
sleep(.042)
if repeatcount > 0:
counter=counter+1
for led in reversed(charlieOrder):
lightLED(charlieLEDS[led])
sleep(.042)
if counter > repeatcount-1:
if repeatcount>0:
raise ValueError('limit reached')
#check pin 5 to see if we need to quit
#pin 5 can be set high by another script to trigger the scanner to stop.gpio.setwarnings(False)
gpio.setup(5,gpio.IN)
if gpio.input(5):
quit()
except KeyboardInterrupt:
print 'interrupted'
except ValueError as err:
print(err.args)
finally:
gpio.cleanup()
Advanced Features
Automatic Stop: A separate script can set GPIO pin 5 HIGH to stop the scanner after the next sweep.
Safety: Always use resistors to protect your LEDs from overcurrent.
import RPi.GPIO as gpio
gpio.setwarnings(False)
gpio.setmode(gpio.BCM)
gpio.setup(5,gpio.OUT)
gpio.output(5,gpio.HIGH)
Final Build
The finished scanner runs on a Raspberry Pi Zero 2 W and produces a smooth, glowing red sweep across all 24 LEDs. The project is housed in a custom 3D‑printed case designed specifically for this build.
A perfect blend of retro sci‑fi charm, modern microcontrollers, and maker‑spirit.
Beginner’s Corner: Charlieplexing (Explained Visually)
Charlieplexing sounds like wizardry the first time you hear it — “control 24 LEDs with only 6 pins?!” — but the core idea is surprisingly simple once you see it.
Let’s break it down with visuals and plain language.
The Big Idea
If you have n microcontroller pins, you can control up to n2−n LEDs.
With 6 pins, that’s: 62−6=30
So a 24‑LED Larson Scanner fits comfortably within that limit.
Why it works
LEDs are directional — electricity only flows one way.
Charlieplexing takes advantage of this by:
Setting one pin HIGH (positive)
Setting one pin LOW (negative)
Leaving all other pins as inputs (disconnected)
Only one LED is wired between those two pins in that direction, so only that LED lights.
Visualizing a Single LED
Imagine two GPIO pins:
Pin A ---->|---- Pin BIf A = HIGH and B = LOW, the LED lights.
If A = LOW and B = HIGH, nothing happens (the LED is reversed).
Now scale it up
With 3 pins, you can already control 6 LEDs:
Pin 1 ---->|---- Pin 2
Pin 2 ---->|---- Pin 1
Pin 1 ---->|---- Pin 3
Pin 3 ---->|---- Pin 1
Pin 2 ---->|---- Pin 3
Pin 3 ---->|---- Pin 2Each arrow is a different LED.
This is the whole trick — every ordered pair of pins represents a unique LED.
With 6 pins (your project)
Here’s a simplified conceptual map:
Pins: 13, 16, 19, 20, 21, 26
13 -> 16 16 -> 13
13 -> 19 19 -> 13
13 -> 20 20 -> 13
13 -> 21 21 -> 13
13 -> 26 26 -> 13
16 -> 19 19 -> 16
16 -> 20 20 -> 16
16 -> 21 21 -> 16
16 -> 26 26 -> 16
...and so onYou don’t need all 30 possible combinations — you just map the 24 you actually soldered.
How the Pi “scans” the LEDs
To light LED #7, for example, the Pi might:
Set Pin 19 HIGH
Set Pin 21 LOW
Set all other pins to input
Then for LED #8, it switches to a different HIGH/LOW pair.
Do this fast enough (hundreds of times per second), and your eyes see a smooth, continuous sweep.
The mental model
Think of Charlieplexing like a city of one‑way streets:
Each pair of pins is a street.
Each LED is a car that can only drive in one direction.
You choose which street is open by setting one end HIGH and the other LOW.
Only one car moves at a time — but if you move them fast enough, it looks like a parade.
Why beginners love this trick
It feels like cheating (in a good way)
It teaches how GPIO modes really work
It scales beautifully to big LED arrays
It’s a perfect “aha!” moment project
Charlieplex Matrix Diagram (6 Pins → 30 Possible LEDs)
With 6 GPIO pins, every ordered pair of pins represents one LED. Here’s the full matrix, visualized as a directional grid:
TO →
13 16 19 20 21 26
FROM ↓
13 — 13→16 13→19 13→20 13→21 13→26
16 16→13 — 16→19 16→20 16→21 16→26
19 19→13 19→16 — 19→20 19→21 19→26
20 20→13 20→16 20→19 — 20→21 20→26
21 21→13 21→16 21→19 21→20 — 21→26
26 26→13 26→16 26→19 26→20 26→21 — How to read this
Each row → column entry is one LED.
For example:
19→21means:Pin 19 = HIGH
Pin 21 = LOW
All other pins = INPUT
The diagonal is blank because you can’t have an LED from a pin to itself.
Optional: Minimalist “wiring map” version
If you want something more compact for the page:
13 → 16,19,20,21,26
16 → 13,19,20,21,26
19 → 13,16,20,21,26
20 → 13,16,19,21,26
21 → 13,16,19,20,26
26 → 13,16,19,20,21This version is great for beginners because it shows the pattern without the grid.