Skip to content

Computational Thinking and Debugging for Physical Computing

Summary

This chapter introduces computational thinking (decomposition, pattern recognition, abstraction, and algorithm design) and shows how those four pillars apply to physical computing projects that sense the world and act on it. It covers the vocabulary of physical computing -- input devices, output devices, sensors, actuators, and the sense-think-act cycle -- along with the debugging and iterative-design mindset (rubber duck debugging, root cause analysis, trial-and-error testing) that every hardware project in this book relies on. It also introduces the programming constructs -- flowcharts, pseudocode, state machines, event-driven programming, polling, and interrupts -- that recur across every hardware tier. Students finishing this chapter will be able to describe a physical computing problem in computational-thinking terms and apply a structured debugging process to a broken circuit or program.

Concepts Covered

This chapter covers the following 26 concepts from the learning graph:

  1. Computational Thinking
  2. Decomposition
  3. Pattern Recognition
  4. Abstraction
  5. Algorithm Design
  6. Algorithm
  7. Physical Computing
  8. Input Device
  9. Output Device
  10. Sensor
  11. Actuator
  12. Feedback Loop
  13. Sense Think Act Cycle
  14. Debugging
  15. Rubber Duck Debugging
  16. Iterative Design
  17. Prototype
  18. Edge Case
  19. Root Cause Analysis
  20. Flowchart
  21. Pseudocode
  22. State Machine
  23. Event Driven Programming
  24. Polling
  25. Interrupt
  26. Trial And Error Testing

Prerequisites

This chapter assumes only the prerequisites listed in the course description.


Hi! I'm Berry.

Berry waving welcome Welcome to Learning STEM with Raspberry Pi Hardware! I'm Berry — a raspberry with a circuit-board leaf cap and a tool belt, because I'm the kind of fruit that builds electronics. I'll be popping up in the margins of every chapter in this book, but I don't show up randomly. I have exactly seven jobs, and you'll learn to recognize me by which one I'm doing:

  1. Welcome you at the start of every chapter — that's what I'm doing right now.
  2. Help you think things through when an idea is the kind that clicks better once someone walks through it with you.
  3. Give you tips — the small moves a working maker would make that nobody bothers to write in the manual.
  4. Warn you gently about the spots where smart students and smart circuits get into trouble.
  5. Encourage you when something looks scary the first time you see it (usually a bug).
  6. Celebrate with you when you've earned it — end of chapter, or a big milestone along the way.
  7. Drop in with a general note when something's worth flagging but doesn't fit the other six jobs.

That's it. If I'm not doing one of those seven things, I'm not in the chapter. Let's build something!

Every superpower starts with a way of thinking, and this chapter is where you pick up your first one: computational thinking. It's the mental toolkit that lets you look at a messy real-world problem — "make this robot stop before it hits the wall," "make this keyboard key glow red when a message arrives" — and turn it into something a $4 microcontroller can actually execute. This chapter also introduces the vocabulary every project in this book shares: the parts that sense the world, the parts that act on it, and the loop that connects them. Finally, it covers something every maker needs and most books skip: what to do when the thing you built doesn't work. Debugging isn't a footnote here — it's the training montage, and by the end of this chapter you'll have a structured way to run it.

What Is Computational Thinking?

Computational thinking is a way of breaking a problem apart and describing a solution so precisely that a machine can carry it out, without any human filling in the gaps. It is not the same as "coding." You can think computationally with a whiteboard, sticky notes, or a conversation, long before you write a single line of Python. Computer scientists usually describe computational thinking as four pillars that work together:

  1. Decomposition — breaking a large, intimidating problem into smaller pieces that are each easy to solve on their own.
  2. Pattern Recognition — noticing similarities between the current problem and problems you (or someone else) have already solved.
  3. Abstraction — deciding which details matter for the problem at hand and ignoring the rest, so you can work with a simpler model of reality.
  4. Algorithm Design — arranging the pieces into an ordered set of steps that reliably produces the result you want.

Consider a project from later in this book: a Pico-based robot that should stop before it hits a wall. Decomposition splits that goal into smaller jobs — measure distance, decide if it's too close, and stop the motors. Pattern recognition notices this is really the same shape as a problem you've already solved elsewhere: "if a number crosses a threshold, do something," which is the same logic a thermostat uses. Abstraction lets you ignore details that don't matter yet, like the exact voltage curve of the distance sensor, and just work with "distance in centimeters." Algorithm design puts the pieces in order: read the sensor, compare it to a limit, stop if needed, repeat.

The ordered set of steps that comes out of that last pillar has its own name: an algorithm. An algorithm is a finite, unambiguous sequence of instructions that takes some input and produces a specific output — the same way a recipe takes ingredients and produces a cake, as long as every step is followed exactly. "Stir until it feels about right" is not an algorithm; "stir for 30 seconds" is, because a computer (or a new cook) can follow it with no guesswork.

Berry's Key Insight

Berry thinking Here's the part that surprises people: the four pillars aren't just for programmers. Decomposition, pattern recognition, abstraction, and algorithm design are how you'd plan a bake sale, a road trip, or a science fair project too. Physical computing just gives you a very honest partner — a $4 chip that runs your algorithm exactly as written, bugs and all.

Before we look at how the four pillars connect, let's put them side by side in one place.

Diagram: Four Pillars of Computational Thinking

Run the Four Pillars of Computational Thinking MicroSim fullscreen

Four Pillars of Computational Thinking (interactive infographic)

Type: interactive-infographic sim-id: four-pillars-of-computational-thinking
Library: p5.js
Status: Specified

Learning objective: Students will explain (Bloom L2: Understand) how decomposition, pattern recognition, abstraction, and algorithm design each contribute to solving a physical computing problem.

Canvas: 700x460px default, responsive — recalculate a cellSize from windowWidth on a windowResized() handler so the layout reflows to a single column below 480px wide and a 2x2 grid above it.

Layout: four rounded-rectangle cards arranged in a 2x2 grid (2x2 above 480px, stacked 4x1 below it). Each card has a distinct accent color drawn from the book's theme palette: Decomposition (indigo #3F51B5), Pattern Recognition (raspberry #C2185B), Abstraction (circuit green #2E7D32), Algorithm Design (copper gold #D4AF37 on a dark card background for contrast). Each card shows a short icon (drawn with basic p5.js shapes, not an image file — e.g., Decomposition: a single large rectangle splitting into three smaller rectangles; Pattern Recognition: three repeating small squares; Abstraction: a detailed shape fading into a simplified silhouette; Algorithm Design: a short vertical flow of three connected steps) and the pillar name.

Interaction: clicking a card flips it (or expands a panel beneath the grid) to reveal a one-sentence physical-computing example tied to a robot that must stop before hitting a wall — one example per pillar, matching the four examples given in the surrounding chapter text. Hovering a card (desktop) or tapping once (touch) highlights its border in a lighter tint of its accent color before a second click/tap opens the example, so the interaction works with both mouse and touch input. Include a small "Reset" button (p5.js createButton) that collapses all four cards back to their closed state.

Implementation: p5.js. State stored in an array of four objects {name, color, example, isOpen}. Draw cards with rect() and rounded corners via the rectMode/corner-radius argument; render text with textAlign(CENTER, CENTER). Use mousePressed() to detect which card was hit via simple rectangle bounds-checking, and toggle that card's isOpen state. Parent the canvas to the enclosing <div> and call a resize routine inside windowResized().

Writing Down an Algorithm: Flowcharts and Pseudocode

Once you've designed an algorithm in your head, you need a way to write it down that's clearer than a paragraph of prose but doesn't yet force you into a specific programming language. Two tools handle that job, and you'll see both throughout this book.

A flowchart is a diagram that represents an algorithm using standardized shapes: an oval for start and end points, a rectangle for a processing step, a diamond for a yes/no decision, and arrows showing which step comes next. Flowcharts are especially good at showing branching — the moment where a program's path depends on a condition, like "is the distance less than 10 centimeters?"

Pseudocode represents the same algorithm as short, plain-language lines that look almost like code but skip the exact punctuation a real programming language demands. Where a flowchart shows shape and flow at a glance, pseudocode is faster to write and easier to translate directly into Python once you're ready. Here is the wall-stopping algorithm written both ways in spirit — as a flowchart it would be four shapes connected by arrows; as pseudocode it reads like this:

REPEAT forever:
    distance = read the distance sensor
    IF distance < 10 centimeters:
        stop the motors
    ELSE:
        keep driving forward

Before we look at an interactive version of this pairing, notice that the pseudocode's IF line and the flowchart's diamond shape represent the exact same decision — that's the connection the next diagram lets you explore by clicking.

Diagram: Flowchart and Pseudocode Explorer

Run the Flowchart and Pseudocode Explorer MicroSim fullscreen

Flowchart and Pseudocode Explorer (interactive diagram)

Type: interactive-diagram sim-id: flowchart-pseudocode-explorer
Library: p5.js
Status: Specified

Learning objective: Students will analyze (Bloom L4: Analyze) the correspondence between a flowchart's graphical steps and the equivalent lines of pseudocode for a simple sense-and-react algorithm.

Canvas: 700x500px, split into two responsive panels — a flowchart panel on the left (or top, on narrow screens below 600px) and a pseudocode panel on the right (or bottom). Use windowResized() to switch between side-by-side and stacked layout at the 600px breakpoint.

Content: render the "stop before the wall" algorithm as four connected flowchart shapes (Start oval → "Read distance sensor" rectangle → "distance < 10 cm?" diamond → two branches: "Stop motors" rectangle on the Yes path, "Keep driving" rectangle on the No path → arrow looping back to the Read step). In the pseudocode panel, render the four corresponding lines of monospaced text shown in the surrounding chapter prose.

Interaction: clicking any flowchart shape highlights it with a colored outline (raspberry red #C2185B) and simultaneously highlights the matching line(s) of pseudocode in the same color. Clicking a pseudocode line does the reverse, highlighting the matching flowchart shape. Only one shape/line pair is highlighted at a time; clicking elsewhere on the canvas clears the highlight. Add a small legend text reading "Click a shape or a line to see its match."

Implementation: p5.js. Store shapes and pseudocode lines as parallel arrays sharing a common matchId field. Use rectangle/point-in-polygon hit-testing in mousePressed() against each shape's bounding box and each text line's bounding box. Redraw the full diagram every frame from state rather than mutating drawn pixels, so highlight state stays consistent after a resize.

The Vocabulary of Physical Computing

Everything in this book falls under one umbrella term: physical computing — writing programs that sense the physical world (light, distance, motion, sound, touch) and respond by changing something in the physical world (light, sound, movement). It's the difference between a program that only moves data around on a screen and one that lights an LED, spins a motor, or reads a real button press.

Physical computing systems are built from two families of parts, connected through a microcontroller or computer that runs your algorithm in between them. An input device is any component that lets a program receive information from the physical world. An output device is any component that lets a program change something in the physical world. Two more specific terms describe input and output devices that do a particular kind of work: a sensor is an input device that measures a physical quantity — light level, distance, temperature, whether a button is pressed — and converts it into a signal a program can read. An actuator is an output device that converts a program's decision back into physical motion, light, or sound — a motor, a NeoPixel LED, a speaker.

Berry's Tip

Berry sharing a tip Every sensor and actuator you'll meet in this book — photoresistors, buttons, NeoPixel strips, OLED screens, servo motors, even a camera module — is just a specific example of these two general categories. Learn the categories once, and every new component you meet gets easier to place.

Now that "sensor" and "actuator" are defined, the table below sorts a handful of components you'll actually wire up later in this book into their correct category.

Component Category What it does
Photoresistor Sensor (input) Measures light level and reports it as a changing voltage
Push button Sensor (input) Reports whether a circuit is open or closed
Time-of-Flight distance sensor Sensor (input) Measures distance to the nearest object by timing a light pulse's reflection
Camera module Sensor (input) Captures images for a program to analyze
NeoPixel LED strip Actuator (output) Displays color and light patterns
Servo motor Actuator (output) Converts a program's command into a precise rotation
OLED display Actuator (output) Shows text, numbers, or simple graphics
Small speaker Actuator (output) Converts a program's command into sound

A sensor and an actuator only become useful together when they're connected by something that decides what to do with the sensor's reading. That connection has a name that shows up constantly in this book: the sense-think-act cycle — read a sensor (sense), run an algorithm on that reading to make a decision (think), and command an actuator based on that decision (act), then repeat the whole cycle. When the "act" step changes something that a later "sense" step will detect — like a robot's motor moving it closer to a wall, which its distance sensor will then read as a smaller number — the system has a feedback loop: the output of one cycle becomes the input to the next.

Berry's Key Insight

Berry thinking Nearly every project in this book — from a blinking NeoPixel to a Raspberry Pi 5 running real-time object detection — is one more example of the sense-think-act cycle running at a different speed, with different hardware. Once this loop clicks for you, every new chapter is really just "same loop, new parts."

Diagram: Sense-Think-Act Cycle Explorer

Run the Sense-Think-Act Cycle Explorer MicroSim fullscreen

Sense-Think-Act Cycle Explorer (MicroSim)

Type: microsim sim-id: sense-think-act-cycle-explorer
Library: p5.js
Template: https://github.com/dmccreary/stem-robots/tree/main/docs/sims/physical-computing-explorer
Status: Specified

Learning objective: Students will understand (Bloom L2: Understand) how a sensor reading, a decision, and an actuator command form a repeating feedback loop in a physical computing system.

Canvas: 700x480px, responsive via windowResized() recalculating node positions as fractions of width/height rather than fixed pixels.

Layout: three large circular nodes arranged in a triangle labeled "Sense" (left, colored raspberry #C2185B), "Think" (top, colored copper gold #D4AF37), and "Act" (right, colored circuit green #2E7D32), connected by curved directional arrows Sense → Think → Act → Sense, forming a visible loop. Inside each node, show a small representative icon: Sense shows a simple sensor glyph, Think shows a small gear/chip glyph, Act shows a simple motor/LED glyph.

Controls (p5.js built-ins per project convention): a createSlider() labeled "Cycle Speed" (range 1–10, default 5) controlling how fast a small animated dot travels around the loop; a createButton() labeled "Run / Pause" toggling the animation; a createButton() labeled "Toggle Feedback Arrow" that shows or hides a highlighted return arrow from Act back to Sense, since feedback loops are optional in some designs but present in most robot behaviors.

Interaction: clicking any of the three nodes pauses the animation and opens a small text panel below the diagram naming two example components for that stage, pulled from the table earlier in this chapter (e.g., clicking "Sense" shows "Example: photoresistor, time-of-flight distance sensor"). Clicking the same node again, or clicking "Run / Pause", resumes the animation and hides the panel.

Implementation: p5.js, animated dot position computed from an accumulating angle variable advanced each frame by an amount derived from the slider value, following a triangular or curved path defined by three anchor points. Node hit-testing via dist() between mouse position and each node's center compared to its radius.

State Machines, Events, Polling, and Interrupts

The sense-think-act cycle explains what happens in one pass through a program's main loop, but most real projects need to remember where they are between passes — a robot behaves differently while it's "driving" than while it's "avoiding an obstacle," even though both states share the same sensors. A state machine is a model of a system that can be in exactly one of a limited number of named conditions, called states, at any moment, and that moves between states only in response to defined triggers. A state machine for a simple robot might have states like "Idle," "Driving," and "Avoiding," where a distance-sensor reading below a threshold triggers the move from "Driving" to "Avoiding."

Those triggers are usually called events, and a program built around reacting to them uses event-driven programming — a style where the program spends most of its time waiting, and specific blocks of code run only when a defined event occurs, rather than the program working through one fixed sequence from top to bottom. There are two different techniques a program can use to actually notice that an event has happened. Polling means the program repeatedly checks a sensor's value in a loop, on a fixed schedule, to see if anything has changed — like glancing at a clock every few seconds to see if it's time to leave. An interrupt is a hardware signal that pauses whatever the processor is doing and immediately jumps to a specific block of code the moment an event occurs, without the program needing to check anything itself — closer to a timer going off and telling you it's time, instead of you checking the clock yourself.

Let's compare the two side by side before looking at how they change a state machine's behavior.

Polling Interrupt
How it detects an event Program actively checks the sensor in a loop Hardware signals the processor the instant the event happens
Response delay Depends on how often the loop checks (can miss brief events) Nearly immediate, even for very short events
CPU usage while waiting Higher — the processor is busy checking, even when nothing has changed Lower — the processor can do other work until interrupted
Good fit for Simple projects, slow-changing sensors like light level Time-critical events, like counting fast button presses

Berry's Gentle Warning

Berry warning Don't assume interrupts are always the "better" choice just because they sound more advanced. Polling is simpler to write, easier to debug, and completely fine for a lot of projects in this book. Save interrupts for the moments a project genuinely can't afford to miss an event.

Diagram: Robot State Machine Simulator

Run the Robot State Machine Simulator MicroSim fullscreen

Robot State Machine Simulator (MicroSim)

Type: microsim sim-id: robot-state-machine-simulator
Library: p5.js
Template: https://github.com/dmccreary/automating-instructional-design/tree/main/docs/sims/state-machine-template
Status: Specified

Learning objective: Students will analyze (Bloom L4: Analyze) how a robot's state machine transitions between states in response to events, and compare how those events are detected under polling versus interrupt-driven designs.

Canvas: 700x480px, responsive layout recalculating node positions from width/height fractions on windowResized().

Layout: a node-link diagram with three state nodes — "Idle" (gray), "Driving" (circuit green #2E7D32), "Avoiding" (raspberry #C2185B) — connected by labeled directional arrows: Idle → Driving ("start button pressed"), Driving → Avoiding ("distance < 10 cm"), Avoiding → Driving ("path clear"), Driving → Idle ("stop button pressed"). The currently active state node is drawn with a thicker highlighted border.

Controls: a createSelect() dropdown labeled "Detection Mode" with two options, "Polling" and "Interrupt" (default: Polling); a createSlider() labeled "Polling Interval (ms)" (range 100–2000, default 500, disabled/grayed out when "Interrupt" is selected); a createButton() labeled "Trigger Event" that simulates the distance sensor crossing its threshold.

Interaction: in Polling mode, clicking "Trigger Event" arms a pending event, but the diagram only transitions states at the next scheduled poll tick — visualized as a small pulse animation sweeping outward from the active node on each tick, drawn at the interval set by the slider. In Interrupt mode, clicking "Trigger Event" transitions the state immediately, visualized as an instant flash on the arrow being followed, with a small on-screen timestamp comparison (e.g., "Polling: reacted in ~340 ms · Interrupt: reacted in ~2 ms") that updates after each trigger so students can directly compare response latency. Clicking any state node opens a short pseudocode snippet beneath the diagram showing that state's IF condition and transition, reusing the same pseudocode style introduced earlier in the chapter.

Implementation: p5.js, millis()-based timers for the polling tick and for measuring simulated interrupt latency. Node and arrow hit-testing via bounding-box or distance checks in mousePressed(). Keep the two detection-mode code paths in clearly separate functions so the "polling delay" versus "interrupt immediacy" behavior stays easy to tell apart while implementing.

Debugging Like an Engineer

Here's a promise worth making at the start of this book: something you build is going to fail. A wire will be in the wrong pin, a variable will be misspelled, a sensor will read backwards from what you expected. That's not a sign you're bad at this — it's the normal, expected texture of building anything real, and every maker in this book's target audience, from a first-time student to a working engineer, goes through it constantly.

Debugging is the process of finding and fixing the cause of unexpected behavior in a program or circuit. Debugging only works when it's structured, so this section covers the specific moves that turn "it's not working and I don't know why" into "found it."

The first move costs nothing and works surprisingly often: rubber duck debugging means explaining your code or circuit out loud, line by line or wire by wire, to another person — or, in a pinch, to an actual rubber duck on your desk. The act of putting your logic into words, slowly and in order, forces you to notice the gap you skipped over silently in your head.

You've Got This!

Berry encouraging you Every great inventor short-circuits a few things first. A blinking LED that refuses to blink, or a robot that spins in the wrong direction, isn't a failure — it's data. Every wire tells a story, and right now yours is just telling you where to look next.

When rubber duck debugging alone doesn't surface the problem, root cause analysis takes over: instead of patching the first symptom you see, you trace backward through the system, step by step, until you find the single original cause that all the symptoms trace back to. A robot that "sometimes" stops for no reason might have five suspects — loose wire, bad sensor, wrong threshold value, low battery, timing bug — and root cause analysis is the discipline of testing them one at a time instead of guessing.

Root cause analysis gets a lot easier once you can name the specific input that breaks things. An edge case is an input or condition at the extreme boundary of what a program was designed to handle — a distance reading of exactly 0, a button held down for the very first frame the program runs, a light sensor in a completely dark room. Programs frequently work fine on "normal" inputs and fail only at the edges, which is exactly why edge cases deserve deliberate testing rather than being left to chance.

Fixing one bug rarely means a project is finished — it means it's ready for another pass. Iterative design is the practice of repeatedly building, testing, and refining a project in short cycles rather than trying to get everything perfect in a single attempt. Each cycle usually produces a prototype: an early, incomplete, working version of a project built specifically to test one idea before investing more time in it. And the method that drives each cycle forward, especially early on, is simply trial and error testing — deliberately trying a change, observing the real result, and using that result (not a guess) to decide what to try next.

Before the closing diagram, it helps to see these five debugging ideas as a single repeating loop rather than a list.

  • Build a small prototype of just the piece you're testing.
  • Run it and observe what actually happens, not what you expected to happen.
  • Isolate the problem using rubber duck debugging and root cause analysis, paying special attention to edge cases.
  • Fix the one specific cause you found — not everything you can think of at once.
  • Repeat the cycle, through trial and error testing, until the behavior matches the goal.

Diagram: Debugging Decision Tree

Run the Debugging Decision Tree MicroSim fullscreen

Debugging Decision Tree (MicroSim)

Type: microsim sim-id: debugging-decision-tree
Library: p5.js
Status: Specified

Learning objective: Students will apply (Bloom L3: Apply) a structured debugging process to select an appropriate strategy for diagnosing a broken circuit or program.

Canvas: 700x520px, responsive — recompute node vertical spacing as a fraction of height in windowResized() so the tree stays legible on narrow screens by narrowing horizontal spacing first before shrinking text.

Layout: a top-down decision tree starting from a root node labeled "It's not working!" branching into "Did it ever work before?" — Yes leads to "What changed most recently?" (pointing to root cause analysis), No leads to "Is the problem in the wiring or the code?" which splits again into "Check wiring: power, ground, correct pins" and "Read the code out loud, line by line" (rubber duck debugging). Both paths converge on a shared bottom node: "Test one small change at a time" (trial and error testing), which loops back with a curved arrow to the root node.

Interaction: every node is clickable; clicking reveals a short tip in an infobox beneath the tree, matching that node's debugging concept as defined in the surrounding chapter text (e.g., clicking "Read the code out loud, line by line" shows a one-sentence rubber-duck-debugging tip). Only one infobox is shown at a time. A createButton() labeled "Reset" collapses any open infobox. Visited nodes remain marked with a small checkmark icon after being clicked, so a student can see which branches of the tree they've already explored.

Implementation: p5.js tree layout computed once at setup and on resize from a small hard-coded node/edge data structure (parent-child pairs with x/y as fractions of canvas size). Hit-testing via rectangular bounds around each node's text. Draw edges as simple bezier or straight lines connecting parent center to child center.

Bringing It Together

Every chapter after this one puts these same ideas to work on new hardware. When you wire up a NeoPixel strip in the next tier of this book, you'll decompose "make it glow" into smaller steps, recognize it as a pattern you've already seen in a simpler blinking LED, and design an algorithm for it. When you build a STEM robot, you'll be looking at a sense-think-act cycle running through a state machine, deciding between polling and interrupts for its sensors. And when any of it refuses to work on the first try — it will — you now have rubber duck debugging, root cause analysis, and trial and error testing ready to go, instead of just guessing.

You Unlocked a Superpower!

Berry celebrating That's berry impressive work! You just picked up the mental toolkit every single project in this book leans on: computational thinking, the sense-think-act cycle, and a real debugging process. STEM is our superpower, and this one's now yours. Let's build something — see you in Chapter 2!

See Annotated References