Skip to content
Draft
4 changes: 4 additions & 0 deletions Documentation/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
169 changes: 169 additions & 0 deletions Documentation/fundamentals/choosing-drivers.md
Original file line number Diff line number Diff line change
@@ -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)
202 changes: 202 additions & 0 deletions Documentation/fundamentals/gpio-basics.md
Original file line number Diff line number Diff line change
@@ -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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot you should mention that this will lead to 15mA current. Therefore a normal 20mA led is expected in this example and that high-brightness leds should not be attached to GPIO directly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 7f680f7 — both the "Driving an LED" section and the best practices list now mention the ~15 mA current with a 220 Ω resistor, that this is fine for standard 20 mA indicator LEDs, and that high-brightness/power LEDs should not be connected to GPIO directly.

## 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)
Loading
Loading