diff --git a/Documentation/README.md b/Documentation/README.md index bb53ca459d..6bac1bc00a 100644 --- a/Documentation/README.md +++ b/Documentation/README.md @@ -31,6 +31,10 @@ While contributing, you should read the [coding guidelines section](https://gith ### General-Purpose Input/Output (GPIO) +* [GPIO Protocol Guide](./protocols/gpio.md) - Quick start, GpioController usage, and troubleshooting +* [GPIO Basics](./fundamentals/gpio-basics.md) - Digital I/O concepts, pull resistors, voltage levels +* [Choosing the Right Driver](./fundamentals/choosing-drivers.md) - LibGpiodDriver vs SysFsDriver comparison +* [Using libgpiod to control GPIOs](./gpio-linux-libgpiod.md) - libgpiod library versions and installation * [GPIO Wiki](https://en.wikipedia.org/wiki/General-purpose_input/output) * [Digital I/O Fundamentals](http://www.ni.com/white-paper/3405/en/#toc1) diff --git a/Documentation/fundamentals/choosing-drivers.md b/Documentation/fundamentals/choosing-drivers.md new file mode 100644 index 0000000000..87b2db9d55 --- /dev/null +++ b/Documentation/fundamentals/choosing-drivers.md @@ -0,0 +1,169 @@ +# Choosing the Right GPIO Driver + +When working with GPIO on Linux systems, .NET IoT offers different driver implementations. This guide helps you understand the differences and choose the right driver for your project. + +## Quick Answer + +For most users, you do not need to choose a driver at all. The parameterless constructor auto-detects the best driver for your board: + +```csharp +// Recommended — works on all supported boards +using GpioController controller = new(); +``` + +Read on if you need to understand what happens under the hood, or if auto-detection does not work for your board. + +## Available Drivers + +### LibGpiodDriver (Recommended) + +Uses the modern `libgpiod` library to access GPIO through the Linux character device interface. + +**Use LibGpiodDriver when:** + +- Using modern Linux kernels (4.8+) +- Running on Raspberry Pi OS (Bullseye, Bookworm, or later) +- Multiple processes may access GPIO simultaneously +- You want the best-maintained and most future-proof solution + +### LibGpiodV2Driver + +Same concept as `LibGpiodDriver`, but targets libgpiod version 2.x (the library's API changed between v1 and v2). + +**Use LibGpiodV2Driver when:** + +- Your system has libgpiod 2.x installed (common on newer distributions) + +### SysFsDriver (Deprecated) + +Uses the older `/sys/class/gpio` interface, which is deprecated in modern Linux kernels. + +**Use SysFsDriver when:** + +- Stuck on very old Linux kernels (pre-4.8) +- **Not recommended for new projects** + +## Driver Comparison + +| Feature | LibGpiodDriver | SysFsDriver | +| --- | --- | --- | +| **Status** | Active, Recommended | Deprecated | +| **Kernel Version** | 4.8+ | All | +| **Performance** | Better | Good | +| **Multi-process** | Safe | Unsafe | +| **Resource Cleanup on crash** | Kernel releases lines automatically when the process exits | Exported pins can linger under `/sys/class/gpio/` if the process is killed | +| **Future Support** | Yes | Deprecated; sysfs GPIO interface is being phased out in the Linux kernel | + +## Auto-Detection + +When you use `new GpioController()`, the framework automatically selects the best driver: + +| Board | Driver selected | +| --- | --- | +| Raspberry Pi 3 / 4 | `RaspberryPi3Driver` (requires root or membership of a privileged group, because it maps `/dev/mem`; if you run without root, pass `new LibGpiodDriver()` explicitly) | +| Raspberry Pi 5 | `LibGpiodDriver`. On recent Raspberry Pi OS kernels the user GPIO chip is `gpiochip0` and auto-detection picks the right chip; older Pi 5 kernels exposed user GPIO on `gpiochip4` and need an explicit chip number (see below) | +| Other Linux boards | Tries `LibGpiodDriver` → `LibGpiodV2Driver` → `SysFsDriver` | + +```csharp +// Let the framework choose the best driver for your board +using GpioController controller = new(); +``` + +## Explicit Driver Selection + +If auto-detection does not work, pass a driver to the `GpioController` constructor: + +```csharp +using System.Device.Gpio; +using System.Device.Gpio.Drivers; + +// LibGpiodDriver for libgpiod v1 (chip number 0 on most boards) +using GpioController controller = new(new LibGpiodDriver(gpioChip: 0)); + +// LibGpiodV2Driver for libgpiod v2 +using GpioController controller = new(new LibGpiodV2Driver(chipNumber: 0)); +``` + +### Finding Your Chip Number + +Different boards expose GPIO on different chip numbers. Use the `gpioinfo` command to list available chips: + +```bash +gpioinfo +``` + +- **Raspberry Pi 3/4:** chip number **0** (`gpiochip0`) +- **Raspberry Pi 5 (recent kernels):** chip number **0** (`gpiochip0`) +- **Raspberry Pi 5 (older kernels):** chip number **4** (`gpiochip4`) — see [Wrong Chip Number (Raspberry Pi 5)](#wrong-chip-number-raspberry-pi-5) + +## Installing libgpiod + +Most Raspberry Pi OS images come with libgpiod pre-installed. If not: + +```bash +sudo apt update +sudo apt install libgpiod2 +``` + +To check which version is installed: + +```bash +apt show libgpiod2 +``` + +For building from source or more detail on library versions, see [Using libgpiod to control GPIOs](../gpio-linux-libgpiod.md). + +## Permission Configuration + +If you get "Permission denied" errors when accessing GPIO: + +```bash +# Add your user to the gpio group +sudo usermod -aG gpio $USER + +# Log out and log back in, then verify +groups +``` + +## Troubleshooting + +### "libgpiod not found" / DllNotFoundException + +```bash +sudo apt install libgpiod2 +``` + +### Wrong Chip Number (Raspberry Pi 5) + +Older Raspberry Pi 5 kernels exposed user GPIO on `gpiochip4` rather than `gpiochip0`. On recent Raspberry Pi OS releases the user GPIO chip is `gpiochip0` and auto-detection works; if you are stuck on an older kernel and auto-detection picks the wrong chip, update your kernel first (`sudo apt update && sudo apt upgrade && sudo reboot`). If updating is not an option, pass the chip number explicitly: + +```csharp +using GpioController controller = new(new LibGpiodDriver(gpioChip: 4)); +``` + +### Driver Version Mismatch + +If you get errors, check your installed libgpiod version and use the matching driver class: + +- libgpiod 0.x–1.x → `LibGpiodDriver` +- libgpiod 2.x → `LibGpiodV2Driver` + +## Summary + +| Situation | What to use | +| --- | --- | +| New project, any supported board | `new GpioController()` | +| Need a specific chip number | `new GpioController(new LibGpiodDriver(gpioChip: N))` | +| System has libgpiod 2.x only | `new GpioController(new LibGpiodV2Driver(chipNumber: N))` | +| Very old kernel (pre-4.8) | `new GpioController(new SysFsDriver())` | + +## Next Steps + +- [GPIO Basics](gpio-basics.md) — Fundamentals of digital I/O, pull resistors, voltage levels +- [GPIO Protocol Guide](../protocols/gpio.md) — Quick start example and GpioController usage +- [libgpiod Usage Guide](../gpio-linux-libgpiod.md) — Detailed libgpiod installation and version info + +## Additional Resources + +- [libgpiod Documentation](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about/) +- [Linux GPIO Documentation](https://www.kernel.org/doc/html/latest/driver-api/gpio/index.html) diff --git a/Documentation/fundamentals/gpio-basics.md b/Documentation/fundamentals/gpio-basics.md new file mode 100644 index 0000000000..c51d796eaf --- /dev/null +++ b/Documentation/fundamentals/gpio-basics.md @@ -0,0 +1,202 @@ +# GPIO Basics + +General-Purpose Input/Output (GPIO) pins are the foundation of IoT hardware interfacing. They are programmable electrical pins on single-board computers like the Raspberry Pi that let your .NET code interact with physical hardware — turning LEDs on and off, reading button presses, and communicating with sensors. + +This guide explains the essential concepts you need to understand when working with GPIO pins in .NET IoT. + +## What is GPIO? + +GPIO pins are programmable pins on your single-board computer that can: + +- **Output digital signals** — Turn devices on/off (LEDs, relays, motors) +- **Read digital signals** — Detect button presses, sensor states +- **Generate PWM signals** — Control brightness, speed (requires specific PWM-capable pins) +- **Communicate via protocols** — I2C, SPI, UART (requires specific pins) + +GPIO pins operate with digital logic levels (HIGH/LOW): + +- **HIGH** = 3.3 V on Raspberry Pi (varies by platform) +- **LOW** = 0 V (Ground) + +## Pin Numbering + +There are two ways to refer to pins, which can be confusing at first: + +| Scheme | Example | Description | +| --- | --- | --- | +| **GPIO / BCM** | `18` | The number used by the Broadcom chip — **this is what .NET IoT uses** | +| **Physical / Board** | `12` | The physical position on the 40-pin header | + +GPIO 18 and physical pin 12 are the **same pin** — just identified differently. Your code always uses **GPIO (BCM) numbers**. + +Use the interactive diagram at [pinout.xyz](https://pinout.xyz/) to look up the mapping for your board. + +## Digital Output + +Digital output means setting a pin to either HIGH (3.3 V) or LOW (0 V). That is all digital output does: voltage on or voltage off. + +```csharp +using System.Device.Gpio; + +using GpioController controller = new(); +controller.OpenPin(18, PinMode.Output); + +controller.Write(18, PinValue.High); // 3.3 V — turns the LED on +controller.Write(18, PinValue.Low); // 0 V — turns the LED off +``` + +You can use this to drive LEDs, buzzers, relays, and any component that responds to a high/low signal. + +### Driving an LED + +LEDs require a current-limiting resistor to prevent damage: + +```text +GPIO Pin ──► LED (Anode/+) ──► LED (Cathode/−) ──► Resistor (220 Ω) ──► Ground +``` + +> **Why a resistor?** LEDs need very little current. Without a resistor, the LED draws too much current and can burn out or damage your board. With a 220 Ω resistor on a 3.3 V output, the current is at most 3.3 V / 220 Ω ≈ 15 mA (less in practice due to the LED's own voltage drop). This is safe for a standard 20 mA indicator LED but **not** for high-brightness or power LEDs — those draw much more current and should be driven through a transistor or MOSFET instead. + +## Digital Input + +Digital input means reading whether a pin is HIGH or LOW. + +```csharp +using System.Device.Gpio; + +using GpioController controller = new(); +controller.OpenPin(17, PinMode.InputPullUp); + +PinValue value = controller.Read(17); +if (value == PinValue.Low) +{ + Console.WriteLine("Button is pressed!"); +} +``` + +### Pull-Up and Pull-Down Resistors + +When a button is **not** pressed, the pin is not connected to anything. In plain `Input` mode, this leaves the pin "floating" — its voltage drifts randomly, giving unreliable readings. + +Pull-up and pull-down resistors solve this by defining a default voltage: + +| Mode | Default state (not pressed) | Pressed state | Wiring | +| --- | --- | --- | --- | +| `InputPullUp` | High (3.3 V) | Low (0 V) | Button connects pin to Ground | +| `InputPullDown` | Low (0 V) | High (3.3 V) | Button connects pin to 3.3 V | + +`InputPullUp` is the most common choice for buttons. The Raspberry Pi has built-in pull-up resistors that are activated when you use this mode — no external resistor required. + +```csharp +controller.OpenPin(17, PinMode.InputPullUp); + +// When button is NOT pressed: reads HIGH +// When button IS pressed: reads LOW (inverted logic) +if (controller.Read(17) == PinValue.Low) +{ + Console.WriteLine("Button pressed"); +} +``` + +### Button Wiring (with InputPullUp) + +```text +GPIO 17 ──► one leg of button +GND ──► other leg of button +``` + +When the button is pressed, GPIO 17 gets connected to Ground (Low). When released, the internal pull-up resistor pulls it back to High. + +## Interrupt-Driven Input (Events) + +Instead of checking the pin over and over in a loop (polling), you can register a callback that fires when the pin's value changes. This is more efficient and more responsive: + +```csharp +using System; +using System.Device.Gpio; +using System.Threading; + +using GpioController controller = new(); +controller.OpenPin(17, PinMode.InputPullUp); + +controller.RegisterCallbackForPinValueChangedEvent( + 17, + PinEventTypes.Falling, // trigger on button press (HIGH → LOW) + (sender, args) => + { + Console.WriteLine($"Button pressed on pin {args.PinNumber}"); + }); + +Console.WriteLine("Press the button. Ctrl+C to exit."); +Thread.Sleep(Timeout.Infinite); +``` + +**Event types:** + +- `PinEventTypes.Rising` — LOW → HIGH transition +- `PinEventTypes.Falling` — HIGH → LOW transition +- `PinEventTypes.Rising | PinEventTypes.Falling` — both directions + +> **Note:** Physical buttons "bounce" — a single press can generate several rapid transitions. If you experience duplicate button events, you need debouncing. A simple approach is to ignore events that arrive within a short time window (e.g., 50 ms) of the previous event. + +## Current and Voltage Limitations + +### Raspberry Pi GPIO Specifications + +- **Output Voltage:** 3.3 V (not 5 V!) +- **Per-pin drive strength:** 8 mA by default on BCM283x SoCs, configurable up to 16 mA. Treat 8 mA as the safe figure unless you have explicitly raised the drive strength. +- **Max total current across all GPIO pins:** model-dependent; on the original Pi the documented limit was ~50 mA, newer models tolerate more. Aim well below the limit and use external drivers for anything power-hungry. +- **Input voltage tolerance:** 0 V to 3.3 V (**5 V will damage the pin!**) + +### Connecting 5 V Devices + +**Never connect 5 V signals directly to GPIO pins!** Use one of these solutions: + +1. **Voltage divider** (for input signals) +2. **Level shifter** (bidirectional, for I2C/SPI) +3. **Transistor/MOSFET** (for high-current outputs) + +### Driving High-Current Devices + +For loads exceeding the per-pin drive strength (motors, relays, high-power LEDs), use a transistor or MOSFET as a switch powered from an external supply. The GPIO pin controls the transistor's gate, not the device directly. + +## Pin Mode Summary + +| PinMode | Description | Typical Use | +| --- | --- | --- | +| `Output` | Drive pin HIGH or LOW | LEDs, control signals | +| `Input` | Read pin state (floating) | Rarely used | +| `InputPullUp` | Read pin, default HIGH | Buttons (active-low) | +| `InputPullDown` | Read pin, default LOW | Sensors (active-high) | + +## Common GPIO Pins on Raspberry Pi + +Not all GPIO pins are suitable for general use: + +- **GPIO 2, 3:** I2C (have hardware pull-up resistors) +- **GPIO 14, 15:** UART (default serial console) +- **GPIO 9, 10, 11:** SPI +- **GPIO 12, 13, 18, 19:** hardware-PWM-capable +- **Safe for general use:** 17, 22, 23, 24, 25, 27 + +**Tip:** Check your specific Raspberry Pi model's pinout at [pinout.xyz](https://pinout.xyz/). + +## Best Practices + +1. **Always dispose of GpioController** — Use `using` statements or call `.Dispose()` +2. **Use `InputPullUp` or `InputPullDown`** — Never leave input pins floating +3. **Respect voltage levels** — Raspberry Pi GPIO operates at 3.3 V; connecting 5 V **will damage the pin** +4. **Use resistors with LEDs** — A 220 Ω resistor on a 3.3 V GPIO pin results in roughly 15 mA, which is fine for a standard 20 mA indicator LED. High-brightness and power LEDs draw much more current and should **not** be connected to a GPIO pin directly — use a transistor or MOSFET instead +5. **Check pin assignments** — Some GPIO pins are shared with I2C, SPI, or UART; using them for general GPIO disables those interfaces +6. **Document your pin usage** — Especially in projects using multiple pins + +## Next Steps + +- [GPIO Protocol Guide](../protocols/gpio.md) — Quick start, GpioController usage, drivers, and troubleshooting +- [Choosing the Right Driver](choosing-drivers.md) — LibGpiodDriver vs SysFsDriver comparison + +## Additional Resources + +- [GPIO Wikipedia](https://en.wikipedia.org/wiki/General-purpose_input/output) +- [Raspberry Pi GPIO Pinout](https://pinout.xyz/) +- [Digital I/O Fundamentals](http://www.ni.com/white-paper/3405/en/#toc1) diff --git a/Documentation/gpio-linux-libgpiod.md b/Documentation/gpio-linux-libgpiod.md index bede52a583..7bd8faee1d 100644 --- a/Documentation/gpio-linux-libgpiod.md +++ b/Documentation/gpio-linux-libgpiod.md @@ -2,27 +2,31 @@ ## Quick usage: blink LED example -This example targets a RaspberryPi 3/4, see comments for more information: +This example targets a Raspberry Pi 3/4, see comments for more information: ```c# -// side note: on the Raspberry Pi the GPIO chip line offsets are the same numbers as the usual BCM GPIO numbering, which is convenient -const int ledGpio = 15; +using System.Device.Gpio; +using System.Device.Gpio.Drivers; +using System.Threading; + +// On the Raspberry Pi the GPIO chip line offsets match BCM GPIO numbering +const int ledGpio = 18; -// on the Pi3,4 you most likely want 0, on the Pi5 number 4, see 'gpioinfo' tool -const int chipNumber = 0; // 'using' will dispose the controller when it falls out of scope, which will un-claim lines +// On Pi 3/4 the parameterless constructor auto-selects the best driver +using var controller = new GpioController(); -// alternatively be more explicit: 'new GpioController(chipNumber, new LibGpiodDriver())' -using var gpioController = new GpioController(chipNumber); +// To explicitly use LibGpiodDriver (e.g. on an older Pi 5 kernel where chip 4 is needed): +// using var controller = new GpioController(new LibGpiodDriver(gpioChip: 4)); -gpioController.OpenPin(ledGpio); +controller.OpenPin(ledGpio, PinMode.Output); for (int i = 0; i < 5; i++) { controller.Write(ledGpio, PinValue.High); - await Task.Delay(1000); + Thread.Sleep(1000); controller.Write(ledGpio, PinValue.Low); - await Task.Delay(1000); + Thread.Sleep(1000); } ``` @@ -44,32 +48,36 @@ Dotnet-iot supports v0, v1 and v2 of libgpiod. The following table shows which driver supports which library version -| LibGpiodDriverVersion | Libgpiod version (documented) | -| --------------------- | ----------------------------------------------------- | -| V1 | 0.x to 1.0.x (Partial support) 1.1 - 1.x (Supported) | -| V2 | 2.x | +| .NET IoT Driver | Libgpiod version (documented) | +| --------------- | ----------------------------------------------------- | +| `LibGpiodDriver` | 0.x to 1.0.x (Partial support) 1.1 - 1.x (Supported) | +| `LibGpiodV2Driver` | 2.x | NOTE: Due to a [breaking change in the values of enums in the libgpiod]( https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/commit/?id=783ff2e3c70788cdd1c65cba9ee0398bda5ebcda), only libgpiod versions 1.1 and later can be expected to function reliably with the V1 driver. To check what libgpiod packages you have on a deb based system, use: ``` $apt show libgpiod* ``` -## Choose LibGpiodDriver Version +## Choose the Right Driver Class -If you want to explicitly select the version of the libgpiod driver, to target a specific library version, there are following options: +.NET IoT provides two separate driver classes for different versions of the libgpiod library: -1. constructor of LibGpiodDriver: +- **`LibGpiodDriver`** — for libgpiod v1 (library versions 0.x through 1.x) +- **`LibGpiodV2Driver`** — for libgpiod v2 (library versions 2.x) - ```c# - new LibGpiodDriver(chipNumber, LibGpiodDriverVersion.V1) - ``` +To use a specific driver: -2. Environment variable: +```c# +using System.Device.Gpio; +using System.Device.Gpio.Drivers; - ```shell - export DOTNET_IOT_LIBGPIOD_DRIVER_VERSION=V1 // or V2... - ``` +// For libgpiod v1 +using var controller = new GpioController(new LibGpiodDriver(gpioChip: 0)); -When not explicitly specified, dotnet iot automatically tries to find a driver compatible to what library version is installed. +// For libgpiod v2 +using var controller = new GpioController(new LibGpiodV2Driver(chipNumber: 0)); +``` + +When not explicitly specified (i.e., when using `new GpioController()`), .NET IoT automatically tries to find a compatible driver for the installed library version. ## Install libgpiod @@ -111,3 +119,9 @@ The installation should be the same on all Pi's, or boards whose distro uses the This will install the library .so files to `/usr/lib/local` If you want to also build command line utilities `gpioinfo, gpiodetect` etc., specify `./autogen.sh --enable-tools=yes` + +## See also + +- [GPIO Protocol Guide](./protocols/gpio.md) — Quick start, `GpioController` usage, drivers, and troubleshooting +- [GPIO Basics](./fundamentals/gpio-basics.md) — Digital I/O concepts, pull resistors, voltage levels +- [Choosing the Right Driver](./fundamentals/choosing-drivers.md) — `LibGpiodDriver` vs `SysFsDriver` comparison diff --git a/Documentation/protocols/gpio.md b/Documentation/protocols/gpio.md new file mode 100644 index 0000000000..f9dbe5075c --- /dev/null +++ b/Documentation/protocols/gpio.md @@ -0,0 +1,349 @@ +# GPIO (General-Purpose Input/Output) + +General-Purpose Input/Output (GPIO) pins are programmable pins on your single-board computer that can be used to interface with external devices. This guide covers how to get started with GPIO in .NET IoT, including a quick-start example, the `GpioController` API, driver selection, and troubleshooting. + +## Contents + +- [What You Need](#what-you-need) +- [Quick Start: Blink an LED](#quick-start-blink-an-led) +- [Pin Numbering](#pin-numbering) +- [Digital Input](#digital-input) +- [Pin Modes](#pin-modes) +- [Reading and Writing](#reading-and-writing) +- [Interrupt-Driven Input (Events)](#interrupt-driven-input-events) +- [Complete Example: Button-Controlled LED](#complete-example-button-controlled-led) +- [GpioController and Drivers](#gpiocontroller-and-drivers) +- [Troubleshooting](#troubleshooting) +- [Best Practices](#best-practices) +- [Related Documentation](#related-documentation) +- [External Resources](#external-resources) + +## What You Need + +- A **Linux single-board computer with GPIO headers** and libgpiod installed (for example, a Raspberry Pi 3, 4, or 5 running Raspberry Pi OS) +- **.NET 8.0 SDK** or later installed ([installation guide](https://learn.microsoft.com/dotnet/core/install/linux-debian)) +- A **breadboard**, a few **jumper wires**, an **LED**, and a **220 Ω resistor** + +> **Tip:** If this is your very first electronics project, search for "breadboard basics" — a breadboard lets you connect components without soldering. + +## Quick Start: Blink an LED + +### 1. Create a new project + +```bash +dotnet new console -n LedBlink +cd LedBlink +dotnet add package System.Device.Gpio +``` + +### 2. Write the code + +Replace the contents of `Program.cs`: + +```csharp +using System; +using System.Device.Gpio; +using System.Threading; + +const int ledPin = 18; // GPIO 18 = physical pin 12 on Raspberry Pi (see https://pinout.xyz/) + +using GpioController controller = new(); + +controller.OpenPin(ledPin, PinMode.Output); + +Console.WriteLine("Blinking LED. Press Ctrl+C to stop."); + +while (true) +{ + controller.Write(ledPin, PinValue.High); // LED on + Thread.Sleep(1000); + controller.Write(ledPin, PinValue.Low); // LED off + Thread.Sleep(1000); +} +``` + +### 3. Wire the circuit + +Connect these components on your breadboard: + +1. **GPIO 18** (physical pin 12) → long leg (anode, +) of the LED +2. Short leg (cathode, −) of the LED → one end of a **220 Ω resistor** +3. Other end of resistor → **Ground** (physical pin 6) + +```text +GPIO 18 ──► LED(+) ──► LED(−) ──► 220 Ω Resistor ──► GND +``` + +> **Why a resistor?** LEDs need very little current. Without a resistor, the LED draws too much current and can burn out or damage your Pi. A 220 Ω resistor limits the current to a safe level. + +### 4. Run + +```bash +dotnet run +``` + +You should see the LED blink once per second. + +## Pin Numbering + +.NET IoT uses **BCM (Broadcom) GPIO numbering**, not physical pin numbers. For example, GPIO 18 corresponds to physical pin 12 on a Raspberry Pi — they are the same pin, just identified differently. Always use GPIO/BCM numbers in your code. + +For a complete mapping, see [pinout.xyz](https://pinout.xyz/). + +## Digital Input + +Digital input is the counterpart to digital output: instead of driving a pin HIGH or LOW, you **read** the voltage present on a pin. This lets you detect external signals — most commonly a button press. + +To read a pin, open it in an input mode and call `Read`: + +```csharp +controller.OpenPin(17, PinMode.InputPullUp); + +PinValue value = controller.Read(17); +if (value == PinValue.Low) +{ + Console.WriteLine("Button pressed"); +} +``` + +A typical button circuit connects the GPIO pin to Ground through the button. When the button is pressed the pin sees LOW; when released the internal pull-up resistor pulls it back to HIGH. + +```text +GPIO 17 ──► one leg of button +GND ──► other leg of button +``` + +### Why InputPullUp? + +When a button is **not** pressed, the pin is not connected to anything. In plain `Input` mode, this leaves the pin "floating" — its voltage drifts randomly, giving unreliable readings. `InputPullUp` activates an internal resistor that holds the pin at HIGH by default, so the only way it goes LOW is when the button physically connects it to Ground. `InputPullDown` works the opposite way (default LOW, button connects to 3.3 V). + +See [GPIO Basics](../fundamentals/gpio-basics.md) for a detailed explanation of pull-up and pull-down resistors. + +## Pin Modes + +GPIO pins can be configured in different modes: + +```csharp +// Output — drive pin HIGH or LOW +controller.OpenPin(18, PinMode.Output); + +// Input with pull-up resistor (default HIGH, button press = LOW) +controller.OpenPin(17, PinMode.InputPullUp); + +// Input with pull-down resistor (default LOW, button press = HIGH) +controller.OpenPin(17, PinMode.InputPullDown); +``` + +## Reading and Writing + +```csharp +// Write to output pin +controller.Write(18, PinValue.High); +controller.Write(18, PinValue.Low); + +// Read from input pin +PinValue value = controller.Read(17); +if (value == PinValue.Low) +{ + Console.WriteLine("Button pressed"); +} +``` + +## Interrupt-Driven Input (Events) + +Instead of continuously polling, use events for efficient input handling: + +```csharp +using System; +using System.Device.Gpio; +using System.Threading; + +using GpioController controller = new(); +controller.OpenPin(17, PinMode.InputPullUp); + +controller.RegisterCallbackForPinValueChangedEvent( + 17, + PinEventTypes.Falling, // trigger on HIGH → LOW (button press) + (sender, args) => + { + Console.WriteLine($"Button pressed on pin {args.PinNumber}"); + }); + +Console.WriteLine("Press button. Ctrl+C to exit."); +Thread.Sleep(Timeout.Infinite); +``` + +**Event types:** + +- `PinEventTypes.Rising` — LOW → HIGH transition +- `PinEventTypes.Falling` — HIGH → LOW transition +- `PinEventTypes.Rising | PinEventTypes.Falling` — both transitions + +> **Note:** Physical buttons "bounce" — a single press can generate several rapid transitions. If you experience duplicate button events, you need debouncing. A simple approach is to ignore events that arrive within a short time window (e.g., 50 ms) of the previous event. + +## Complete Example: Button-Controlled LED + +This example turns an LED on while a button is held down: + +```csharp +using System; +using System.Device.Gpio; +using System.Threading; + +const int buttonPin = 17; +const int ledPin = 18; + +using GpioController controller = new(); + +controller.OpenPin(ledPin, PinMode.Output); +controller.OpenPin(buttonPin, PinMode.InputPullUp); + +Console.WriteLine("Hold the button to light the LED. Ctrl+C to exit."); + +while (true) +{ + // Button pressed (Low) → turn LED on (High) + // Button released (High) → turn LED off (Low) + PinValue buttonState = controller.Read(buttonPin); + controller.Write(ledPin, buttonState == PinValue.Low ? PinValue.High : PinValue.Low); + Thread.Sleep(50); +} +``` + +## GpioController and Drivers + +The `GpioController` class is the main entry point for GPIO operations. It abstracts the underlying platform-specific implementation through **GPIO drivers**. You usually do not need to think about this — the parameterless constructor automatically picks the best driver: + +```csharp +// Recommended — auto-detects the best driver for your board +using GpioController controller = new(); +``` + +### Available Drivers + +| Driver | When to use | +| --- | --- | +| `LibGpiodDriver` | Modern Linux with libgpiod v1 — the recommended default | +| `LibGpiodV2Driver` | Modern Linux with libgpiod v2 | +| `RaspberryPi3Driver` | Raspberry Pi 3/4 — very fast (direct memory access). Requires root or membership of a privileged group because it maps `/dev/mem`. Auto-selected on Pi 3/4 when available; if you are running without root, pass `new LibGpiodDriver()` explicitly instead. | +| `SysFsDriver` | Works on any Linux version but very slow — **deprecated** in favor of libgpiod-based drivers | + +### Auto-Detection + +When you use `new GpioController()`, the framework selects automatically: + +- **Raspberry Pi 3/4:** `RaspberryPi3Driver` (falls back to `LibGpiodDriver` when direct memory access is not available; see note above about root permissions) +- **Raspberry Pi 5:** `LibGpiodDriver`. On recent Raspberry Pi OS kernels the user GPIO chip is `gpiochip0` and auto-detection picks the right chip. Older Pi 5 kernels exposed user GPIO on `gpiochip4` — see [Wrong GPIO Chip on Raspberry Pi 5](#wrong-gpio-chip-on-raspberry-pi-5) below if that affects you. +- **Other Linux boards:** Tries `LibGpiodDriver` → `LibGpiodV2Driver` → `SysFsDriver` + +For an in-depth comparison, see [Choosing the Right Driver](../fundamentals/choosing-drivers.md). + +### Manual Driver Selection + +If auto-detection does not work for your board: + +```csharp +using System.Device.Gpio; +using System.Device.Gpio.Drivers; + +// Specify LibGpiodDriver with chip number (most boards use 0) +using GpioController controller = new(new LibGpiodDriver(gpioChip: 0)); +``` + +### Installing libgpiod + +Most Raspberry Pi OS images come with libgpiod pre-installed. If not: + +```bash +sudo apt update +sudo apt install libgpiod2 +``` + +For more detail on libgpiod versions and building from source, see [Using libgpiod to control GPIOs](../gpio-linux-libgpiod.md). + +## Troubleshooting + +### "Permission denied" Error + +```text +System.UnauthorizedAccessException: Access to GPIO is denied +``` + +**Solution:** Add your user to the `gpio` group, then **log out and back in**: + +```bash +sudo usermod -aG gpio $USER +``` + +### "libgpiod not found" Error + +```text +System.DllNotFoundException: Unable to load shared library 'libgpiod' +``` + +**Solution:** Install libgpiod: + +```bash +sudo apt install libgpiod2 +``` + +### Pin Already in Use + +```text +System.InvalidOperationException: Pin 18 is already in use +``` + +**Solutions:** + +1. Another process is using the pin — stop it or reboot +2. Pin not properly disposed in previous run — ensure you use `using` statements +3. Pin reserved by kernel (I2C, SPI, etc.) — use a different pin + +### GPIO Operations Have No Effect / Wrong Pin + +Make sure you are using **BCM/GPIO numbers**, not physical pin numbers. GPIO 18 is physical pin 12 — they are different numbers for the same pin. Consult [pinout.xyz](https://pinout.xyz/). + +### Wrong GPIO Chip on Raspberry Pi 5 + +Older Raspberry Pi 5 kernel versions exposed user GPIO on chip **4** instead of the standard chip **0**. If GPIO operations have no effect on a Pi 5, first try updating your kernel: + +```bash +sudo apt update && sudo apt upgrade +sudo reboot +``` + +If updating is not an option, you can detect the correct chip by looking at the number of lines (pins) on each chip — the user GPIO chip typically has around 54 lines: + +```bash +for chip in /dev/gpiochip*; do + echo "$chip: $(gpioinfo "$chip" | wc -l) lines" +done +``` + +Then pass that chip number when constructing the driver: + +```csharp +// Use the chip number from the detection script above +using GpioController controller = new(new LibGpiodDriver(gpioChip: 4)); +``` + +## Best Practices + +1. **Use `using` statements** — Ensures proper disposal and cleanup of GPIO resources +2. **Use `InputPullUp` or `InputPullDown`** — Avoid floating inputs +3. **Check voltage levels** — Raspberry Pi uses 3.3 V; connecting 5 V **will damage the pin** +4. **Use resistors with LEDs** — 220 Ω is a safe default +5. **Document pin usage** — Keep track of which pins are used for what + +## Related Documentation + +- [GPIO Basics](../fundamentals/gpio-basics.md) — Detailed explanation of digital I/O, pull resistors, voltage levels, and current limits +- [Choosing the Right Driver](../fundamentals/choosing-drivers.md) — In-depth comparison of LibGpiodDriver, LibGpiodV2Driver, and SysFsDriver +- [Using libgpiod to control GPIOs](../gpio-linux-libgpiod.md) — libgpiod library versions and installation from source + +## External Resources + +- [GPIO Wikipedia](https://en.wikipedia.org/wiki/General-purpose_input/output) +- [Raspberry Pi GPIO Pinout](https://pinout.xyz/) +- [libgpiod Documentation](https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/about/) +- [Linux GPIO Documentation](https://www.kernel.org/doc/html/latest/driver-api/gpio/index.html)