Part
2
  |  
The Hardware Layer
  |  
Chapter
5

Circuits for Software People

Hardware isn't analog chaos — it's function calls with voltage as the argument and current as the return value.
Reading Time
10
mins
BACK TO RASPBERRY PI MASTERCLASS

The trap is believing hardware is fundamentally different from software. Engineers who write Python all day stare at a breadboard like it's an alien artifact — wires everywhere, no syntax highlighting, no stack traces. They assume electronics requires years of EE coursework and an oscilloscope. It doesn't. A circuit is a function call. Voltage goes in, current comes out, and there's a defined interface between the two. Once you see that, the breadboard stops being scary and starts being a prototyping surface that's faster than spinning up a new Docker container.

A circuit is a function call. Voltage goes in, current comes out, and there's a defined interface between the two.

I've seen this pattern where developers freeze the moment they leave their terminal. They'll SSH into a Pi, write a Python script in ten minutes, then spend an hour trying to figure out where to plug the LED. The problem isn't intelligence — it's that nobody gave them the mental model that maps hardware concepts to software concepts they already know. That's what this chapter does.

The Voltage Function

Framework · The Voltage Function · VF

Every circuit is a function call: voltage is the argument, current is the return value, and resistance is the implementation. Change the argument (voltage), the implementation changes the output (current). If you understand def voltage_function(v, r): return v / r, you understand Ohm's law.

This is the single most important reframe in the entire hardware section. Ohm's law — V = I * R — is taught as physics. It's actually a function signature. Voltage (V) is measured in volts. Current (I) is measured in amps. Resistance (R) is measured in ohms. Rearrange it to I = V / R, and you have a pure function: given a voltage and a resistance, the current is deterministic.

Here's why this matters practically. The Raspberry Pi's GPIO pins output 3.3 volts. An LED has a forward voltage drop of about 2.0 volts (red LEDs) and needs roughly 20 milliamps of current to light up without burning out. The math:

# Ohm's law as a Python function
def required_resistance(supply_voltage, component_voltage_drop, target_current_amps):
    """Calculate the resistor value needed to protect a component."""
    voltage_across_resistor = supply_voltage - component_voltage_drop
    resistance = voltage_across_resistor / target_current_amps
    return resistance

# Pi GPIO = 3.3V, red LED drops ~2.0V, target 20mA
r = required_resistance(3.3, 2.0, 0.020)
print(f"Use a {r:.0f} ohm resistor (nearest standard: 68 ohm)")
# Output: Use a 65 ohm resistor (nearest standard: 68 ohm)

That's it. A 68-ohm resistor (the nearest standard value you can buy) sits between the GPIO pin and the LED, and it limits the current to a safe level. Without it, the LED draws too much current, heats up, and dies. The resistor is a rate limiter — the same concept as throttling API requests.

Key takeaway

A resistor is a rate limiter. It throttles current the same way you'd throttle API calls — to protect the thing on the other end from being overwhelmed.

The Breadboard: Your Hardware IDE

A breadboard is a prototyping surface. No soldering, no permanent connections. You push component legs and wires into holes, and the breadboard connects them internally according to a fixed pattern. Understanding that pattern is like understanding how imports work in Python — once you know the rules, you stop guessing.

Here's the layout. A standard breadboard has three distinct zones:

Power rails run along the top and bottom edges, marked with red (+) and blue (-) lines. Every hole in a power rail is connected horizontally across the entire length of the board. You connect your power supply's positive terminal to the red rail and ground to the blue rail. Think of these as global constants — available everywhere, defined once.

Terminal strips fill the center of the board. Each row of five holes (labeled a through e on one side, f through j on the other) is internally connected. But the two sides are separated by a center channel — the gap running down the middle. Row 1, holes a-b-c-d-e are all connected. Row 1, holes f-g-h-i-j are all connected. But 1a and 1f are not connected to each other.

The center channel is sized to fit a DIP (dual inline package) integrated circuit — the chip straddles the gap so each pin gets its own connected row. For our purposes with the Pi, just know that the channel splits left from right.

The highway analogy

Think of the breadboard as a highway system. The power rails are the interstate — they run the full length. Each row is a side street — five houses (holes) all on the same block, all connected. The center channel is the median — you can't cross it without a jumper wire bridge.

When you plug a wire from a GPIO pin into hole 10a, anything else plugged into 10b, 10c, 10d, or 10e is now electrically connected to that pin. To connect to the other side, you'd run a jumper wire from, say, 10e across the channel to 10f.

Your First Component Kit

You need exactly four things to build every project in this part of the book:

  • Jumper wires — pre-cut wires with pins on each end. Male-to-male wires connect breadboard holes to each other. Male-to-female wires connect the Pi's GPIO header pins to the breadboard. These are your import statements — they connect one module to another.
  • LEDs — light-emitting diodes. They have a long leg (anode, positive) and a short leg (cathode, negative). Polarity matters. Plug them in backward and they don't light up. They won't explode — diodes block reverse current — but they won't work either.
  • Resistors — the rate limiters. The colored bands encode the resistance value. For LED projects with a 3.3V Pi, grab a bag of 220-ohm resistors. They're slightly over-specced for red LEDs (which would be fine with 68 ohm), but 220 ohm works safely with any LED color. Over-speccing a resistor just makes the LED slightly dimmer. Under-speccing it kills the LED.
  • Tactile push buttons — momentary switches. Press and the circuit closes; release and it opens. They have four legs: the two legs on each side are always connected, and pressing the button connects the two sides. They straddle the center channel of the breadboard.
✕ Software debugging
  • Stack trace points to the line
  • Error is recoverable
  • Worst case: process crash, restart
✓ Hardware debugging
  • No stack trace — use a multimeter
  • Reversed polarity = dead component
  • Worst case: burned GPIO pin, $45 board replacement

That comparison isn't meant to scare you. It's meant to make you respect the one rule hardware enforces that software doesn't: polarity and voltage are not forgiving. In software, a wrong function argument throws an exception. In hardware, a wrong voltage destroys the component. This is why every chapter in this part starts with the circuit before the code.

Reading a Circuit Diagram

A circuit diagram is a directed graph. Nodes are components (resistors, LEDs, buttons). Edges are wires. Current flows from high voltage to low voltage — from the GPIO pin through the components to ground. If you can read a dependency graph or a state machine diagram, you can read a circuit.

The symbols you'll see in this book:

  • A zigzag line is a resistor.
  • A triangle with a line is a diode (LED). Current flows in the direction the triangle points.
  • Two parallel lines are a capacitor (you'll meet these later).
  • A line connecting to ground is marked with three horizontal lines getting shorter — the "ground" symbol.

If you can read a dependency graph, you can read a circuit diagram. Nodes are components, edges are wires, and current flows from high to low.

Here's the mental model I use: trace the circuit from power to ground, the same way you'd trace a request from the client to the database and back. At each component, ask: what does this component do to the signal? A resistor reduces current. An LED converts current to light (and drops some voltage in the process). A button either passes or blocks current depending on whether it's pressed.

The one concept from circuit diagrams that trips up software engineers is the series vs parallel distinction. Components in series sit one after another on the same path — current flows through A, then B, then C. Components in parallel are wired across the same two points — current splits and flows through A and B simultaneously. An LED and its resistor are in series (current flows through both). Two LEDs wired to the same GPIO pin but with separate resistors are in parallel (current splits between them).

Why this matters: in a series circuit, the current is the same everywhere but voltage divides across components. In a parallel circuit, the voltage is the same across each branch but current divides. When you calculate resistor values, you need to know which configuration you're in. For our purposes — single LEDs on individual GPIO pins — everything is series, and the math from The Voltage Function applies directly.

The First Circuit You'll Build

Before we get to code, here's the complete description of the first circuit you'll build in the next chapter. This is the "Hello, World" of hardware:

  1. A male-to-female jumper wire from GPIO 17 on the Pi's header to hole 10a on the breadboard.
  2. A 220-ohm resistor with one leg in hole 10b and the other in hole 15b (spanning five rows of separation for readability — you could use adjacent rows, but spread-out circuits are easier to debug).
  3. The LED's long leg (anode) in hole 15c and the short leg in hole 15 on the other side of the center channel — actually, keep it simple: long leg in 15c, short leg in 20c.
  4. A jumper wire from hole 20d to the breadboard's blue (ground) power rail.
  5. A male-to-female jumper wire from the ground rail to any GND pin on the Pi's header (pin 6 is the closest).

That's five connections. The current path: GPIO 17 outputs 3.3V, current flows through the jumper wire, through the 220-ohm resistor (which drops about 1.3V and limits current to safe levels), through the LED (which drops about 2.0V and emits light), then back to the Pi through ground. The voltage drops sum to 3.3V — Kirchhoff's voltage law.

Common first-timer mistakes

Three things go wrong with this circuit: the LED is backward (no light, but no damage — flip it), the resistor legs are in the same breadboard row (short circuit — spread them across rows), or the ground wire isn't connected (no complete circuit — no current flows). If the LED doesn't light up, check these three things in order.

GPIO Pins Are Function Calls

The Raspberry Pi's 40-pin header is the hardware API. Each pin has a specific function — some supply power (5V, 3.3V), some are ground, and 26 of them are General Purpose Input/Output (GPIO) pins you can control from Python.

When your Python script sets a GPIO pin to HIGH, the Pi's processor pushes that pin to 3.3 volts. When you set it to LOW, the pin drops to 0 volts (ground). That's the entire interface. HIGH is 3.3V. LOW is 0V.

from gpiozero import LED

# This is a function call that sets GPIO 17 to HIGH (3.3V)
led = LED(17)
led.on()   # Pin 17 is now outputting 3.3 volts

# This is a function call that sets GPIO 17 to LOW (0V)
led.off()  # Pin 17 is now at ground potential

The gpiozero library abstracts the voltage into method calls. But underneath, led.on() is telling the Pi's BCM2835 chip to set the register for GPIO 17 to output mode and drive it high. It's a system call — ioctl on a file descriptor — wrapped in a Python object.

Key takeaway

Every GPIO interaction — on, off, read, PWM — is a system call to the Pi's memory-mapped hardware registers, wrapped in Python for your convenience. The abstraction is clean, but the voltage underneath is real.

Pin numbering

The Pi has two numbering schemes: BCM (Broadcom chip numbering) and BOARD (physical position on the header). BCM 17 is physical pin 11. This book uses BCM everywhere because gpiozero defaults to BCM, the Raspberry Pi documentation uses BCM, and mixing schemes is the fastest way to wire things wrong. Ignore BOARD numbering. Pretend it doesn't exist.

Here's a quick reference for the pins you'll use most in this part of the book:

# Quick GPIO reference — the pins used in Part 2
gpio_map = {
    17: "Digital output / LED (Chapters 5-6)",
    27: "Secondary output / button input (Chapters 6-7)",
    22: "Third output / traffic light (Chapter 6)",
    18: "Hardware PWM channel 0 (Chapter 8-9)",
    23: "Additional output (Chapter 7)",
}

# Power pins (not GPIO — always on)
power_pins = {
    1: "3.3V power",
    2: "5V power",
    6: "Ground (GND) — use this one",
    9: "Ground (GND)",
    14: "Ground (GND)",
}

The 40-pin header has eight ground pins, two 3.3V pins, and two 5V pins. The rest are GPIO. You'll never run out of ground connections, which is good because every circuit needs one.

The Multimeter: Your Debugger

If the breadboard is your IDE, the multimeter is your debugger. A $15 digital multimeter does three things you'll use constantly:

Continuity mode (the beep setting) tells you whether two points are electrically connected. Touch the probes to both ends of a wire — beep means connected, silence means broken. Use this to verify your breadboard connections are where you think they are.

Voltage mode (DC, the V with a straight line) tells you the voltage difference between two points. Touch the red probe to a GPIO pin set HIGH and the black probe to ground — you should read 3.3V. If you read 0V, the pin isn't configured as output. If you read something between 0 and 3.3V, the pin might be floating or your circuit is drawing too much current.

Resistance mode (the omega symbol) tells you the resistance of a component. Touch both probes to a resistor's legs — the display shows its value in ohms. This is faster than decoding the color bands, especially when your color perception is uncertain under workshop lighting.

I use the multimeter more than any other physical tool. When a circuit doesn't work, the first question is always "is the voltage where I expect it?" — and the multimeter answers that in two seconds.

What to Do Monday Morning

Buy a starter kit

Get a Raspberry Pi starter kit that includes a breadboard, jumper wires, LEDs, resistors (220 ohm), and push buttons. CanaKit and Freenove both sell kits under $20 that include everything for this part of the book. Don't buy components individually — you'll spend more time shopping than building.

Map the breadboard with a multimeter

Set your multimeter to continuity mode (the beep setting). Touch one probe to hole 1a and the other to 1e — it should beep (connected). Touch 1a and 1f — no beep (separated by the center channel). Touch any hole on the red power rail and another hole ten columns away on the same rail — beep. This ten-minute exercise will do more for your understanding than any diagram.

Calculate a resistor value

Open a Python REPL and implement the required_resistance function from this chapter. Calculate the correct resistor for a red LED (2.0V drop, 20mA) and a blue LED (3.2V drop, 20mA) on a 3.3V supply. Notice how the blue LED barely has any voltage headroom — that's why blue LEDs are dimmer on a Pi than on an Arduino (which runs at 5V).

Identify the GPIO pins on your Pi

Run pinout in your Pi's terminal — it ships with Raspberry Pi OS and prints a labeled diagram of every pin. Screenshot it or print it. Tape it next to your desk. You'll reference it every time you wire something for the next several chapters.

The breadboard is your hardware IDE — no compilation step, no permanent commits, and every wire is a reversible decision.