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.
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.
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.
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.
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.
You need exactly four things to build every project in this part of the book:
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.
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:
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.
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:
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.
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.
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.
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.
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.
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.
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.
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.
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).
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.