Project 3: Interrupt-Driven Button (Edge Events)

React to button presses using edge events and debounce without busy polling.

Quick Reference

Attribute Value
Difficulty Level 2: Intermediate
Time Estimate 8-12 hours
Main Programming Language C (Alternatives: Python)
Alternative Programming Languages Python
Coolness Level Level 2: Solid Engineering
Business Potential 2. The “Micro-SaaS / Pro Tool”
Prerequisites GPIO basics, Linux CLI, basic electronics
Key Topics edge events, debounce, line bias, timestamps

1. Learning Objectives

By completing this project, you will:

  1. Explain the core question: How do you react to real-world events without wasting CPU?
  2. Implement the main workflow for Interrupt-Driven Button.
  3. Handle at least two error conditions gracefully.
  4. Validate output against a deterministic demo.

2. All Theory Needed (Per-Concept Breakdown)

Edge Events and Debounce

Fundamentals Edge Events and Debounce describes interrupt-driven GPIO input with software filtering. At a high level, you must understand the data path, the electrical constraints, and the software interface that exposes the hardware. This concept is the foundation for the project because it explains how a simple action in user space becomes a physical effect on the pin or bus. You should be able to describe the signal direction, the timing expectations, and the role of configuration steps such as pinmux, bus speed, or line bias. Without that mental model, debugging feels like guesswork.

In Interrupt-Driven Button (Edge Events), the key topics (edge events, debounce, line bias, timestamps) are not abstract terms; they are the exact levers you will pull to make hardware behave. A correct mental model includes the electrical layer (voltage, current, pull-ups), the interface layer (GPIO/I2C/SPI/UART/PWM), and the software layer (drivers, sysfs, ioctl calls). If you cannot describe how a change at one layer affects the others, you will end up debugging by trial and error. That is why this project starts with fundamentals: once you can trace a signal from your code to the pin, every bug becomes a solvable puzzle rather than a mystery.

Deep Dive into the concept In practice, Edge Events and Debounce requires disciplined control over configuration and timing. For this project, the focus is interrupt-driven GPIO input with software filtering. That means you must know which register or interface controls the hardware, how to configure it safely, and how to observe the result with real measurements. The hardware path is never abstract: signals travel on wires, devices have limits, and the Linux kernel enforces ownership. When you send a command, the driver or peripheral executes it on a particular clock domain with specific setup and hold times. If those constraints are violated, you see intermittent failures rather than clean errors.

A good deep dive also includes error handling. You must plan for negative acknowledgments, busy devices, or timing delays. For example, bus transactions can fail if the target does not respond or if the clock is too fast. GPIO control can fail if the pin is configured for an alternate function. These are not edge cases; they are common in field deployments. Building robust systems means explicitly detecting these conditions and responding with retries, fallback behavior, or clear error messages.

Finally, connect the concept to verification. You should measure the actual waveform or data, not just trust your code. For buses, this means using tools like i2cdetect, a logic analyzer, or debug prints of raw bytes. For timing, it means timestamps and jitter calculations. For actuation, it means observing the physical device. When you can correlate your code to those measurements, you truly understand the concept and can scale it to larger systems.

A deeper look at Interrupt-Driven Button (Edge Events) starts with sequencing. Even simple hardware interactions require a strict order: configure the interface, validate the device, perform the transaction, and only then interpret results. The key topics here ({key_topics}) each have parameters that must be chosen deliberately, such as bus speed, pin mode, edge polarity, or timing period. When these are wrong, failures can look random. The discipline is to set conservative defaults, verify each step with a minimal test (like reading a device ID or toggling a pin), and then increase complexity gradually. This mirrors real-world bring-up procedures on embedded boards, where one wrong assumption can waste hours.

Failure modes deserve special attention. Wiring errors, missing pull-ups, incorrect voltage levels, and pinmux conflicts are more common than software bugs. At the protocol layer, you may see NACKs, framing errors, or corrupted samples caused by wrong timing. At the OS layer, permission errors, device file contention, or missing overlays can block access to the hardware. A robust implementation anticipates these failures: it checks return codes, times out cleanly, and reports exactly what went wrong. In production systems, these checks are the difference between an intermittent field failure and a diagnosable incident.

Verification is the final pillar of a deep dive. You should measure the signal or data path using appropriate tools: a multimeter for steady voltages, a logic analyzer for digital waveforms, or an oscilloscope for pulse timing and noise. For bus protocols, capturing raw bytes or frames lets you correlate what the hardware saw with what your code expected. For timing-critical outputs, measure jitter and compare against your requirements. If you cannot measure it, you cannot improve it. This project explicitly includes deterministic demos and logging so your results can be reproduced and compared across changes.

How this fits on projects This concept is central to this project and directly informs the implementation choices you make.

Definitions & key terms

  • Edge event: Transition from low to high or high to low.
  • Debounce: Filter out rapid transitions from mechanical switches.
  • Bias: Pull-up or pull-down resistors to stabilize inputs.

Mental model diagram

Button press -> bounce -> kernel interrupt -> event queue -> user handler

How it works (step-by-step)

  1. Request GPIO line with edge detection
  2. Configure pull-up/down bias
  3. Wait for event using poll
  4. Filter events by time window

Minimal concrete example

if (elapsed_ms(last, event.ts) > 20) handle_press();

Common misconceptions

  • Debounce is only hardware -> software debounce often suffices
  • Any edge equals a real press -> bounce creates extra edges

Check-your-understanding questions

  1. Why do buttons bounce?
  2. Why is edge-driven input more efficient than polling?
  3. How does pull-up wiring affect edge polarity?

Check-your-understanding answers

  1. Mechanical contacts oscillate before settling.
  2. Polling wastes CPU and scales poorly.
  3. Pressing a pull-up button creates a falling edge.

Real-world applications

  • Panel buttons
  • Limit switches

Where you’ll apply it

References

  • Linux GPIO character device docs

Key insights Edge Events and Debounce is the pivot between theory and reliable hardware behavior.

Summary The project succeeds when you can explain and verify interrupt-driven GPIO input with software filtering end-to-end.

Homework/Exercises to practice the concept

  1. Measure bounce duration with a logic analyzer.

Solutions to the homework/exercises

  1. Capture the line and measure edge burst length.

3. Project Specification

3.1 What You Will Build

A complete implementation of Interrupt-Driven Button (Edge Events) that directly controls hardware or reads data and presents it in a clear output format.

3.2 Functional Requirements

  1. Core Functionality: Implement the primary data/control loop.
  2. Configuration: Accept CLI arguments or config file.
  3. Error Handling: Detect and report failures with exit codes.
  4. Logging: Provide observable output for debugging.

3.3 Non-Functional Requirements

  • Performance: Meets timing or throughput expectations for the device.
  • Reliability: Recovers from common failures without undefined behavior.
  • Usability: Clear CLI flags and readable logs.

3.4 Example Usage / Output

./button_events --gpio 17 --debounce-ms 20

3.5 Data Formats / Schemas / Protocols

Timestamped log lines.

3.6 Edge Cases

  • Floating input
  • Line already in use

3.7 Real World Outcome

You can run the program and observe correct hardware behavior along with logs or readings that match the physical device.

3.7.1 How to Run (Copy/Paste)

cd project-root
make
./button_events --gpio 17 --debounce-ms 20

3.7.2 Golden Path Demo (Deterministic)

Use a fixed wiring setup and the default CLI options to produce a repeatable output.

3.7.3 If CLI: exact terminal transcript

$ ./button_events --gpio 17 --debounce-ms 20
[INFO] Running Interrupt-Driven Button (Edge Events)
[INFO] OK
$ echo $?
0

Failure Demo (Deterministic)

$ ./button_events --bad-arg
[ERROR] Invalid argument
$ echo $?
2

4. Solution Architecture

4.1 High-Level Design

Input/Bus -> Driver Layer -> Application Logic -> Output/Actuator

4.2 Key Components

| Component | Responsibility | Key Decisions | |———–|—————-|—————| | IO Layer | Talks to hardware | Uses kernel interfaces | | Logic | Parses/controls | Small state machine | | Output | Logs/acts | CLI output or actuation |

4.3 Data Structures (No Full Code)

struct State { int running; int error; /* device-specific fields */ };

4.4 Algorithm Overview

Key Algorithm: Main Loop

  1. Initialize device.
  2. Perform read/write cycle.
  3. Validate and log output.
  4. Repeat or exit.

Complexity Analysis: O(n) iterations, O(1) memory.


5. Implementation Guide

5.1 Development Environment Setup

sudo apt-get update
sudo apt-get install -y build-essential

5.2 Project Structure

project-root/
├── src/main.c
├── Makefile
└── README.md

5.3 The Core Question You’re Answering

“How do you react to real-world events without wasting CPU?”

5.4 Concepts You Must Understand First

  1. edge events
  2. debounce
  3. line bias
  4. timestamps

5.5 Questions to Guide Your Design

  1. What are the safe electrical limits of your device?
  2. Which interface parameters must be configured (speed, mode, bias)?
  3. How will you verify correct behavior with measurements?

5.6 Thinking Exercise

Sketch the full data path from your program to the physical signal or sensor reading.

5.7 The Interview Questions They’ll Ask

  1. Explain edge events in this project.
  2. What failures did you handle and how?
  3. How would you make this production-ready?

5.8 Hints in Layers

Hint 1: Start with known-good wiring and default parameters. Hint 2: Verify with a logic analyzer or multimeter. Hint 3: Log raw bytes or timestamps before converting.

5.9 Books That Will Help

| Topic | Book | Chapter | |——-|——|———| | GPIO events | The Linux Programming Interface | Ch. 49 | | Debounce and timing | Making Embedded Systems | Ch. 6 |

5.10 Implementation Phases

Phase 1: Bring-up (3-4 hours)

Goals: Hardware responds to basic commands. Checkpoint: First successful read/write.

Phase 2: Core Functionality (4-6 hours)

Goals: Full data/control loop. Checkpoint: Stable output.

Phase 3: Robustness (2-4 hours)

Goals: Error handling and logging. Checkpoint: Clean exit codes and clear logs.

5.11 Key Implementation Decisions

| Decision | Options | Recommendation | Rationale | |———-|———|—————-|———–| | Interface | default, custom | default | Minimize variables | | Logging | stdout, file | stdout | Simplicity |


6. Testing Strategy

6.1 Test Categories

| Category | Purpose | Examples | |———-|———|———-| | Unit | parsing/config | CLI flags | | Integration | hardware IO | on Pi | | Edge | invalid args | error paths |

6.2 Critical Test Cases

  1. Golden path: deterministic demo succeeds.
  2. Bad argument: exits with code 2.
  3. Hardware missing: clear error message.

6.3 Test Data

default args, invalid args

7. Common Pitfalls & Debugging

7.1 Frequent Mistakes

| Pitfall | Symptom | Solution | |———|———|———-| | Wrong wiring | No response | Re-check pinout | | Wrong config | Garbage data | Match device settings | | No logging | Hard to debug | Add raw logs |

7.2 Debugging Strategies

  • Verify wiring with a multimeter.
  • Inspect kernel logs with dmesg.

7.3 Performance Traps

Excessive logging or busy loops can degrade timing.


8. Extensions & Challenges

8.1 Beginner Extensions

  • Add a simple status LED
  • Add config file support

8.2 Intermediate Extensions

  • Add retry logic
  • Add CSV/JSON export

8.3 Advanced Extensions

  • Add hardware timestamping
  • Port to a lower-level driver

9. Real-World Connections

9.1 Industry Applications

  • Prototyping: fast sensor or actuator validation
  • Diagnostics: field testing
  • libgpiod or spidev examples

9.3 Interview Relevance

  • Demonstrates hardware interface knowledge and error handling

10. Resources

10.1 Essential Reading

  • Raspberry Pi documentation
  • Device datasheet for your sensor/actuator

10.2 Video Resources

  • Vendor tutorials and bus protocol overviews

10.3 Tools & Documentation

  • i2c-tools, spidev, or libgpiod documentation
  • P01-sysfs-legacy-blink.md
  • P02-register-blink-mmio.md

11. Self-Assessment Checklist

11.1 Understanding

  • I can explain the signal path
  • I understand key failure modes

11.2 Implementation

  • All functional requirements met
  • Error paths tested

11.3 Growth

  • I can extend this to a larger system

12. Submission / Completion Criteria

Minimum Viable Completion:

  • Core workflow works on real hardware
  • Deterministic demo reproducible

Full Completion:

  • Error handling and logging complete
  • Documentation with wiring notes

Excellence (Going Above & Beyond):

  • Performance measurements and optimization
  • Integration into a larger system