Project 1: Sysfs Legacy Blink (Understanding Deprecated GPIO)

Build a minimal LED blink using the legacy sysfs GPIO interface and learn exactly how file writes become voltage changes.

Quick Reference

Attribute Value
Difficulty Level 1: Beginner
Time Estimate 4-8 hours
Main Programming Language Bash + C (Alternatives: Python)
Alternative Programming Languages Python
Coolness Level Level 1: Starter
Business Potential 1. The “Resume Gold”
Prerequisites Linux CLI basics, BCM vs physical pin map, Ohm’s law
Key Topics sysfs GPIO, permissions, electrical safety, pin numbering

1. Learning Objectives

By completing this project, you will:

  1. Export, configure, and toggle a GPIO line through /sys/class/gpio and explain each step.
  2. Map a physical header pin to a BCM GPIO number without guesswork.
  3. Calculate a safe LED resistor value and verify current draw.
  4. Explain why sysfs GPIO is deprecated and what replaces it.

2. All Theory Needed (Per-Concept Breakdown)

Linux Sysfs GPIO Interface (Legacy Hardware-as-Files)

Fundamentals The sysfs GPIO interface is the historic way Linux exposed hardware control through the filesystem. It creates files like export, direction, and value under /sys/class/gpio, and writing text to those files instructs the kernel to change pin configuration or output level. This matters because it demonstrates Linux’s “everything is a file” abstraction: user space does not access registers directly; it requests actions by writing to kernel-managed pseudo-files. The interface is deprecated because it is racy, lacks atomic configuration, and cannot express modern GPIO features like line bias, debounce, or timestamped events. Even so, it is a great teaching tool because it makes the control path visible and easy to experiment with using simple shell commands.

Deep Dive into the concept Sysfs GPIO is implemented by the gpiolib subsystem as a compatibility layer that exports GPIO lines to a virtual filesystem. When you echo 17 > /sys/class/gpio/export, the kernel creates /sys/class/gpio/gpio17/ and a set of attribute files. Each file write becomes a kernel callback that validates permissions, updates a line configuration, and issues register operations on the underlying gpiochip driver. For example, writing out to direction sets the line to output; writing 1 or 0 to value calls a set() function on the driver which drives the line high or low. Because sysfs uses separate files for direction, value, edge, and active_low, it cannot update multiple properties atomically. If two processes race to change direction and value, you can see transient glitches. This is one reason sysfs was deprecated in favor of the GPIO character device API, which bundles line configuration into a single ioctl call.

The sysfs API also hides critical details. Line bias (pull-up or pull-down) is not explicit. Debounce is absent. Edge events are delivered as file reads but lack precise timestamps because they are user-space read time, not kernel time. Another issue is numbering: sysfs uses global GPIO numbers that are not stable across platforms. On Raspberry Pi, people often confuse BCM numbering with physical pin numbers, and sysfs does not help. The char device API exposes line names and per-chip numbering, which is safer and more descriptive. Sysfs also ignores exclusive ownership; two processes can write the same GPIO value file without coordination, which is dangerous in real systems.

Despite these flaws, sysfs is still useful for controlled experiments. It lets you prove that your wiring is correct and verify that a pin can be driven high or low. It also teaches filesystem semantics: sysfs files do not store data; they trigger immediate hardware operations. This is why tools like strace show writes to sysfs files returning success even though no persistent data exists. In practice, your LED blink loop is a tight loop of sysfs writes, and you can observe CPU usage, filesystem overhead, and scheduling jitter. The result is a very clear picture of why newer APIs exist.

Finally, sysfs teaches about permissions. The gpio group often owns the sysfs files. If your process lacks permission, you get EACCES. This is a real-world lesson: hardware is privileged, and Linux enforces policy at the file permission level. For embedded devices, you often create udev rules or systemd units to grant access. Understanding this now will save you time later when you build daemons that run as non-root users.

A deeper look at Sysfs Legacy Blink (Understanding Deprecated GPIO) 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.

How this fits on projects This concept is the backbone of Project 1. It also provides historical context for Projects 3 and 4, where you will compare sysfs behavior to the modern libgpiod approach.

Definitions & key terms

  • sysfs: A virtual filesystem for kernel objects and attributes.
  • gpiolib: Linux kernel subsystem that abstracts GPIO controllers.
  • gpiochip: A kernel object representing a GPIO controller.
  • export/unexport: sysfs operations that create or remove line files.
  • line direction: The configuration that sets a line as input or output.

Mental model diagram

User Space
  echo "1" > /sys/class/gpio/gpio17/value
        |
        v
sysfs attribute handler (kernel)
        |
        v
gpiolib -> gpiochip driver -> SoC register write
        |
        v
GPIO pin voltage changes

How it works (step-by-step, with invariants and failure modes)

  1. echo 17 > /sys/class/gpio/export requests ownership of GPIO17.
  2. Kernel creates /sys/class/gpio/gpio17/ and attribute files.
  3. echo out > direction sets line to output mode.
  4. echo 1 > value drives the line high.
  5. echo 0 > value drives the line low.

Invariants: Only exported lines appear in sysfs. Writing invalid values returns errors. Failure modes: Permission denied, wrong GPIO number, line already claimed by another driver.

Minimal concrete example

# Blink GPIO17 via sysfs
sudo sh -c 'echo 17 > /sys/class/gpio/export'
sudo sh -c 'echo out > /sys/class/gpio/gpio17/direction'
while true; do
  echo 1 | sudo tee /sys/class/gpio/gpio17/value >/dev/null
  sleep 0.5
  echo 0 | sudo tee /sys/class/gpio/gpio17/value >/dev/null
  sleep 0.5
done

Common misconceptions

  • “sysfs files store state.” -> They are live kernel attributes.
  • “GPIO numbers are physical pin numbers.” -> On Pi, BCM and physical differ.
  • “sysfs is the recommended way.” -> It is deprecated in favor of the char device API.

Check-your-understanding questions

  1. Why is sysfs considered non-atomic for GPIO configuration?
  2. What happens if two processes write to the same value file?
  3. Why might GPIO17 not appear after writing to export?

Check-your-understanding answers

  1. Direction, bias, and edge are separate files, so updates can race.
  2. The last write wins; no ownership or locking is enforced by sysfs.
  3. The GPIO might be reserved by a driver or sysfs might be disabled.

Real-world applications

  • Quick hardware bring-up in the lab
  • Debugging wiring before writing production code
  • Teaching the Linux device model to new engineers

Where you’ll apply it

References

  • Linux GPIO sysfs deprecation notes (kernel documentation)
  • Raspberry Pi GPIO docs (pin numbering and electrical limits)
  • “How Linux Works, 3rd Edition” - Brian Ward, Ch. 1-3

Key insights Sysfs GPIO is a powerful teaching tool but is too limited and unsafe for modern production use.

Summary Sysfs reveals the control path from a file write to a hardware register write. It teaches permissions, GPIO numbering, and the limitations that led to the modern character device API.

Homework/Exercises to practice the concept

  1. Export three different GPIOs and list their sysfs directories.
  2. Toggle a GPIO using a shell loop and measure CPU usage.
  3. Use strace to observe sysfs writes from a C program.

Solutions to the homework/exercises

  1. echo 17 > export, echo 27 > export, echo 22 > export then ls /sys/class/gpio.
  2. Run top or htop while the loop runs; observe the busy process.
  3. strace -e write ./blink_sysfs and inspect the write calls.

3. Project Specification

3.1 What You Will Build

A command-line LED blink tool that drives a GPIO line via /sys/class/gpio. It will export the GPIO, set direction, and toggle the line at a configured period. It will also include a cleanup step to unexport the line.

3.2 Functional Requirements

  1. Export GPIO: The program must export the chosen BCM GPIO line.
  2. Configure Direction: The program must set the line as output.
  3. Blink Loop: The program must toggle the line at a configurable rate.
  4. Cleanup: On exit, the program must unexport the GPIO.

3.3 Non-Functional Requirements

  • Performance: 1 Hz blink should be stable within +/- 10%.
  • Reliability: Cleanup must run on normal exit and SIGINT.
  • Usability: User can pass --gpio and --period-ms.

3.4 Example Usage / Output

$ sudo ./sysfs_blink --gpio 17 --period-ms 500
[INFO] Exported GPIO17
[INFO] Direction set to out
[INFO] Blinking at 500 ms
^C
[INFO] Unexported GPIO17

3.5 Data Formats / Schemas / Protocols

  • sysfs file writes: ASCII values written to export, direction, value.

3.6 Edge Cases

  • GPIO already exported: should continue after verifying direction.
  • Permission denied: print error and exit code 2.
  • sysfs not present: exit code 3 and hint to use libgpiod.

3.7 Real World Outcome

You can toggle a real LED and verify with a multimeter that the pin switches between 0 V and 3.3 V.

3.7.1 How to Run (Copy/Paste)

cd project-root
make
sudo ./sysfs_blink --gpio 17 --period-ms 500

3.7.2 Golden Path Demo (Deterministic)

  • Use a fixed GPIO (17) and period (500 ms).
  • Confirm the LED blinks at ~1 Hz.

3.7.3 If CLI: exact terminal transcript

$ sudo ./sysfs_blink --gpio 17 --period-ms 500
[INFO] Exported GPIO17
[INFO] Direction set to out
[INFO] Blinking at 500 ms
^C
[INFO] Unexported GPIO17
$ echo $?
0

Failure Demo (Deterministic)

$ ./sysfs_blink --gpio 999
[ERROR] GPIO not present on this board
$ echo $?
2

4. Solution Architecture

4.1 High-Level Design

CLI args -> Sysfs GPIO layer -> gpiochip driver -> GPIO pin

4.2 Key Components

| Component | Responsibility | Key Decisions | |———–|—————-|—————| | CLI parser | Read gpio/period args | Simple flag parsing | | Sysfs GPIO | Export/config/write/unexport | Use text writes with error checks | | Signal handler | Cleanup on SIGINT | Ensure unexport |

4.3 Data Structures (No Full Code)

struct BlinkConfig {
  int gpio;
  int period_ms;
};

4.4 Algorithm Overview

Key Algorithm: Blink Loop

  1. Write 1 to value.
  2. Sleep period/2.
  3. Write 0 to value.
  4. Sleep period/2.

Complexity Analysis:

  • Time: O(n) toggles
  • Space: O(1)

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/
│   └── sysfs_blink.c
├── Makefile
└── README.md

5.3 The Core Question You’re Answering

“How does Linux turn file writes into voltage changes on a pin?”

5.4 Concepts You Must Understand First

Stop and research these before coding:

  1. BCM vs physical numbering
  2. sysfs file semantics
  3. LED current limiting

5.5 Questions to Guide Your Design

  1. How will you ensure the GPIO is unexported on exit?
  2. How will you validate the GPIO number?
  3. How will you verify the LED actually changes state?

5.6 Thinking Exercise

Draw a diagram of the control path from your shell command to the GPIO register.

5.7 The Interview Questions They’ll Ask

  1. Why is sysfs deprecated?
  2. What is the difference between GPIO numbering schemes?
  3. Why can sysfs cause race conditions?

5.8 Hints in Layers

Hint 1: Use pinout to confirm BCM number. Hint 2: Write to export, then direction. Hint 3: Use trap in bash or signal handlers in C for cleanup.

5.9 Books That Will Help

| Topic | Book | Chapter | |——-|——|———| | Linux basics | How Linux Works | Ch. 1-3 | | Device files | The Linux Programming Interface | Ch. 13 | | Electronics safety | Making Embedded Systems | Ch. 2 |

5.10 Implementation Phases

Phase 1: Foundation (1-2 hours)

Goals: Export GPIO and toggle manually. Tasks: Use echo commands to blink. Checkpoint: LED blinks from shell.

Phase 2: Core Functionality (2-3 hours)

Goals: Implement C program with CLI flags. Tasks: Parse args, write sysfs files. Checkpoint: C program blinks LED.

Phase 3: Polish & Edge Cases (1-2 hours)

Goals: Cleanup, error handling. Tasks: Handle invalid GPIO and permissions. Checkpoint: Proper exit codes.

5.11 Key Implementation Decisions

| Decision | Options | Recommendation | Rationale | |———-|———|—————-|———–| | Language | Bash, C | C | Better control and error handling | | Cleanup | manual, signal handler | signal handler | Always unexport on Ctrl+C |


6. Testing Strategy

6.1 Test Categories

| Category | Purpose | Examples | |———-|———|———-| | Unit Tests | Validate parsing | CLI arg tests | | Integration Tests | Verify sysfs writes | Run on Pi | | Edge Case Tests | Permissions, missing sysfs | Run as non-root |

6.2 Critical Test Cases

  1. Valid GPIO: LED blinks at 1 Hz.
  2. Invalid GPIO: Program exits with code 2.
  3. Missing sysfs: Program exits with code 3.

6.3 Test Data

GPIO=17, period=500ms
GPIO=999, period=500ms

7. Common Pitfalls & Debugging

7.1 Frequent Mistakes

| Pitfall | Symptom | Solution | |———|———|———-| | Wrong numbering | LED never blinks | Use pinout and BCM numbers | | No permissions | “Permission denied” | Use sudo or add to gpio group | | sysfs missing | No /sys/class/gpio | Use libgpiod instead |

7.2 Debugging Strategies

  • Use a multimeter to verify pin voltage.
  • Check dmesg for GPIO allocation errors.

7.3 Performance Traps

  • Busy loops in bash consume CPU; add sleep calls.

8. Extensions & Challenges

8.1 Beginner Extensions

  • Add a --count flag to blink N times.
  • Add a --duty parameter for on/off ratio.

8.2 Intermediate Extensions

  • Support multiple GPIOs at once.
  • Log timestamps to a file.

8.3 Advanced Extensions

  • Replace sysfs with libgpiod and compare latency.
  • Build a simple service that blinks on boot.

9. Real-World Connections

9.1 Industry Applications

  • Factory test fixtures: quick LED or relay tests during manufacturing.
  • Board bring-up: verify pin routing after soldering.
  • libgpiod: Modern GPIO API replacing sysfs.
  • pigpio: Low-level GPIO control library for Pi.

9.3 Interview Relevance

  • GPIO control concepts often appear in embedded interviews.
  • Shows understanding of Linux device interfaces.

10. Resources

10.1 Essential Reading

  • “How Linux Works” by Brian Ward - Ch. 1-3 (devices and sysfs)
  • “The Linux Programming Interface” by Michael Kerrisk - Ch. 13 (file I/O)

10.2 Video Resources

  • Raspberry Pi Foundation GPIO basics tutorials

10.3 Tools & Documentation

  • pinout: CLI pin mapping tool
  • Raspberry Pi GPIO docs: pin numbering and limits
  • P02-register-blink-mmio.md: Learn register-level control
  • P03-interrupt-button-edge.md: Move from polling to events

11. Self-Assessment Checklist

11.1 Understanding

  • I can explain how sysfs maps file writes to GPIO drivers
  • I can map BCM numbers to physical pins
  • I understand why sysfs is deprecated

11.2 Implementation

  • LED blinks reliably
  • Cleanup runs on exit
  • Errors are handled with exit codes

11.3 Growth

  • I can explain this project in an interview
  • I documented wiring and resistor values

12. Submission / Completion Criteria

Minimum Viable Completion:

  • LED blinks at 1 Hz using sysfs
  • Correct BCM pin verified
  • Clean exit/unexport works

Full Completion:

  • CLI arguments for GPIO and period
  • Error handling for permissions and missing sysfs

Excellence (Going Above & Beyond):

  • Compare sysfs vs libgpiod latency with measurements
  • Add systemd service for autostart