Project Spec Sheet & Overview

A Raspberry Pi lamp stack (often called an Andon light or traffic signal tower) is the ultimate physical dashboard for software teams. Instead of refreshing a browser to check if a GitHub Actions workflow or Jenkins build failed, you wire a 12V industrial signal tower to your Pi and let the hardware do the shouting. This guide targets the modern Raspberry Pi 5 (4GB) running Raspberry Pi OS (Bookworm, 64-bit). The Pi 5's new RP1 GPIO chip changes how we interact with hardware, making legacy libraries obsolete and requiring a modernized approach.

ParameterSpecification
DifficultyIntermediate (Requires basic wiring and Python)
Time to Build45 minutes (hardware) + 30 minutes (software)
Estimated Cost$95 - $110 USD
Target BoardRaspberry Pi 5 (4GB) with Official Active Cooler
OS RequirementRaspberry Pi OS Bookworm (64-bit)

Hardware BOM & Pin Mapping

You cannot drive a 12V industrial lamp stack directly from the Pi's 3.3V GPIO headers. Attempting to do so will instantly fry the RP1 chip. We use an opto-isolated relay module to keep the 12V load completely electrically isolated from the Pi's low-voltage logic.

Bill of Materials

  • Microcontroller: Raspberry Pi 5 (4GB) - ~$60
  • Lamp Stack: Generic 12V 3-Tier LED Signal Tower (Red/Yellow/Green + Buzzer) - ~$18
  • Switching: 4-Channel 5V Opto-Isolated Relay Module (Active LOW) - ~$6
  • Lamp Power: 12V 2A Switching Power Supply (barrel jack or hardwired) - ~$8
  • Wiring: Female-to-Female Dupont jumpers (for Pi) and 18 AWG stranded wire (for 12V side) - ~$5

Pin Mapping Table

The Pi 5 uses BCM GPIO numbering. Standard relay modules trigger on a LOW signal, so we will configure our Python code to invert the logic.

Pi 5 Physical PinBCM GPIORelay Module PinLamp Function
37GPIO 26IN1Red Light
35GPIO 19IN2Yellow Light
33GPIO 13IN3Green Light
31GPIO 6IN4Buzzer
25V PowerVCCRelay Coil Power
6GroundGNDCommon Ground

Wiring the 12V Industrial Stack Light

Safety Callout: While 12V DC is generally safe to touch, shorting the 12V power supply directly to the Pi's GPIO pins will permanently destroy the board. Double-check your multimeter readings before connecting power.
  1. Prepare the Relay Module: Connect the Pi's 5V (Pin 2) to the relay module's VCC, and Pi GND (Pin 6) to the relay module's GND. Do not connect the signal pins yet.
  2. Wire the 12V Load Side: Most generic tower lights have a common positive wire (usually black or white) and individual negative wires for each color (Red, Yellow, Green, Buzzer). Connect the 12V PSU positive terminal to the Common (COM) terminal of Relay 1, 2, 3, and 4. Use wire nuts or Wago connectors to daisy-chain the COM terminals.
  3. Connect the Switched Legs: Connect the Normally Open (NO) terminal of Relay 1 to the Red wire of the lamp stack. Repeat for IN2 (Yellow), IN3 (Green), and IN4 (Buzzer).
  4. Complete the Circuit: Connect all the negative wires from the lamp stack directly to the 12V PSU negative terminal.
  5. Connect Logic Pins: With the Pi powered off, connect the female Dupont jumpers from Pi GPIO 26, 19, 13, and 6 to IN1, IN2, IN3, and IN4 on the relay module.
  6. Verify: Power on the 12V PSU first. The lights should remain off. Power on the Pi. The relay module's status LEDs should illuminate faintly (indicating the opto-isolators are receiving 5V standby power), but the relays should not click.

Python Control Code (gpiozero)

On Raspberry Pi OS Bookworm, the legacy RPi.GPIO library is deprecated and incompatible with the Pi 5's RP1 chip. We use gpiozero, which automatically routes through the rpi-lgpio backend. Install the dependencies via terminal: sudo apt install python3-gpiozero python3-rpi-lgpio python3-requests.

This script polls a mock CI/CD API endpoint every 15 seconds. It includes robust error handling for both network timeouts and GPIO faults.


import time
import requests
from gpiozero import DigitalOutputDevice
from gpiozero.exc import GPIOZeroError

# BCM Pin Definitions
PIN_RED = 26
PIN_YELLOW = 19
PIN_GREEN = 13
PIN_BUZZER = 6

# CI/CD API Endpoint (Replace with your GitHub Actions/Jenkins URL)
API_URL = 'https://api.your-ci-provider.com/v1/builds/latest/status'
API_TOKEN = 'your_secure_token_here'

def initialize_relays():
    # Standard relay modules are Active LOW.
    # active_high=False means setting value=1 turns the relay OFF.
    red = DigitalOutputDevice(PIN_RED, active_high=False, initial_value=False)
    yellow = DigitalOutputDevice(PIN_YELLOW, active_high=False, initial_value=False)
    green = DigitalOutputDevice(PIN_GREEN, active_high=False, initial_value=False)
    buzzer = DigitalOutputDevice(PIN_BUZZER, active_high=False, initial_value=False)
    return red, yellow, green, buzzer

def set_status(lights, status):
    red, yellow, green, buzzer = lights
    # Turn everything off first
    for light in lights:
        light.off() # 'off' on active_high=False sends HIGH (relay open)
    
    if status == 'success':
        green.on()
    elif status == 'running':
        yellow.on()
    elif status == 'failed':
        red.on()
        buzzer.on()
        time.sleep(0.5)
        buzzer.off() # Short beep for failure
    else:
        # Unknown status, blink yellow
        yellow.blink(on_time=0.5, off_time=0.5)

def main():
    try:
        lights = initialize_relays()
        print('GPIO initialized successfully. Polling CI/CD status...')
    except GPIOZeroError as e:
        print(f'FATAL: GPIO Initialization failed. Check permissions and pin factory. Error: {e}')
        return

    while True:
        try:
            headers = {'Authorization': f'Bearer {API_TOKEN}'}
            response = requests.get(API_URL, headers=headers, timeout=5)
            response.raise_for_status()
            data = response.json()
            
            # Adjust this key based on your actual API response
            build_status = data.get('status', 'unknown').lower()
            set_status(lights, build_status)
            
        except requests.exceptions.RequestException as e:
            print(f'Network error polling API: {e}')
            set_status(lights, 'unknown') # Failsafe to blinking yellow
        except Exception as e:
            print(f'Unexpected error: {e}')
            
        time.sleep(15)

if __name__ == '__main__':
    main()

Debugging: "Cannot determine SOC peripheral base address"

If you copied legacy code from an older tutorial, you will likely hit a brick wall on the Pi 5. The exact error string thrown is:

RuntimeError: Cannot determine SOC peripheral base address

This happens because the script is trying to import RPi.GPIO, which attempts to map the memory addresses of the legacy BCM2835/2711 chips. The Pi 5 uses the RP1 southbridge chip, which has a completely different memory map.

The First Three Things to Check

  1. Are you using legacy RPi.GPIO? Uninstall it immediately (pip3 uninstall RPi.GPIO). Rewrite your code using gpiozero as shown above. It is the officially supported library by the Raspberry Pi Foundation.
  2. Is the rpi-lgpio backend installed? gpiozero needs a pin factory to talk to the hardware. On Bookworm, this is rpi-lgpio. Verify it by running apt list --installed | grep lgpio. If missing, run sudo apt install python3-rpi-lgpio.
  3. Does your user have GPIO permissions? Run groups in the terminal. You must see gpio in the list. If not, add your user: sudo usermod -aG gpio $USER, then log out and log back in.

Extending and Simplifying the Build

Not every environment needs a 12V industrial tower. Here is how to adjust the build based on your actual constraints.

Simplify (The Desktop Approach): If you just want a desk toy for your home office, ditch the relays and 12V PSU. Buy a pre-wired USB traffic light (like the Delcom USB LED indicator). You can control it directly via Python using the pyusb library, eliminating all GPIO wiring.

Extend (The Silent Approach): Mechanical relays click loudly, which is annoying in a quiet office. To build a silent Raspberry Pi lamp stack, replace the relay module with a 4-Channel MOSFET Driver Board (like the Adafruit 4-Channel I2C MOSFET Driver or a generic IRF520 module). MOSFETs switch silently and can handle PWM, allowing you to dim the lights or create a "breathing" effect for the yellow "building" state using gpiozero.PWMOutputDevice.

Frequently Asked Questions

Can I power the Raspberry Pi lamp stack directly from the GPIO pins?

No. The Raspberry Pi 5 GPIO pins operate at 3.3V logic and can safely source a maximum of 16mA per pin (with a strict total board limit). A single tier of a 12V industrial LED lamp stack typically draws between 60mA and 120mA at 12V. Connecting it directly will result in a dim light at best, and a dead RP1 chip at worst. You must use a switching mechanism like a relay or MOSFET.

How do I connect a PATLITE 24V signal tower to the Raspberry Pi?

PATLITE is the industry standard for manufacturing Andon lights, and many of their models (like the LR6 series) run on 24V DC. The Pi-side logic remains exactly the same, but you must swap the 12V power supply for a 24V DC supply. Crucially, verify that your relay module's contacts are rated for 24V DC (most standard 5V Arduino relay modules are rated for 10A at 30V DC, which is sufficient). If you use MOSFETs, ensure the gate threshold voltage is compatible with 3.3V logic and the drain-source breakdown voltage exceeds 24V.

Why does my relay module chatter or buzz when the Pi boots?

During the Raspberry Pi boot sequence, the GPIO pins are in a high-impedance (floating) state before the OS loads the device tree and initializes the pin drivers. This floating state can cause the opto-isolator on the relay module to partially trigger, resulting in relay chatter. To fix this, add a 10kΩ pull-up resistor between each GPIO signal pin and the 3.3V pin. This forces the pin HIGH (which turns the active-LOW relay OFF) until your Python script takes control and explicitly drives it LOW.

Can I use an ESP32 instead of a Raspberry Pi for the lamp stack?

Yes, but the architecture changes entirely. An ESP32 is a microcontroller, not a Linux computer. You lose the ability to easily run standard Python requests libraries or complex OAuth2 authentication flows required by modern CI/CD APIs like GitHub Actions. If you use an ESP32, you will need to write C++ (Arduino IDE) or MicroPython, use the HTTPClient library, and handle JSON parsing manually. For simple webhook-triggered lights, an ESP32 is cheaper and uses less power; for polling complex REST APIs, the Raspberry Pi is vastly superior. For more on microcontroller protocols, see the gpiozero documentation for Pi-specific implementations.