From 42862efa33541c7c26ec5d2beb1d0755966636f5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 15:42:51 +0000 Subject: [PATCH 1/7] Add GPIO beginners guide and fix incorrect API references in libgpiod docs Agent-Logs-Url: https://github.com/dotnet/iot/sessions/125709c9-1313-48ad-8388-a71a2989a941 Co-authored-by: krwq <660048+krwq@users.noreply.github.com> --- Documentation/README.md | 2 + Documentation/gpio-beginners-guide.md | 326 ++++++++++++++++++++++++++ Documentation/gpio-linux-libgpiod.md | 55 +++-- 3 files changed, 359 insertions(+), 24 deletions(-) create mode 100644 Documentation/gpio-beginners-guide.md diff --git a/Documentation/README.md b/Documentation/README.md index bb53ca459d..f325ada9e0 100644 --- a/Documentation/README.md +++ b/Documentation/README.md @@ -31,6 +31,8 @@ While contributing, you should read the [coding guidelines section](https://gith ### General-Purpose Input/Output (GPIO) +* [GPIO Guide for Beginners](./gpio-beginners-guide.md) - Getting started with GPIO using .NET IoT +* [Using libgpiod to control GPIOs](./gpio-linux-libgpiod.md) - Advanced libgpiod driver configuration * [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/gpio-beginners-guide.md b/Documentation/gpio-beginners-guide.md new file mode 100644 index 0000000000..ad3d36431f --- /dev/null +++ b/Documentation/gpio-beginners-guide.md @@ -0,0 +1,326 @@ +# GPIO Guide for Beginners + +General-Purpose Input/Output (GPIO) pins are programmable electrical pins on single-board computers like the Raspberry Pi. They let your .NET code interact with physical hardware — turning LEDs on and off, reading button presses, and communicating with sensors. + +This guide will walk you through everything you need to know to get started with GPIO using the .NET IoT library. + +## What You Need + +- 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**, a **220 Ω resistor**, and a **push button** + +> **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 + +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 LED +2. Short leg (cathode, −) of LED → one end of **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. + +## Key Concept: 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: Controlling Devices + +Setting a pin to **High** sends 3.3 V to the pin. Setting it to **Low** sends 0 V. That is all digital output does: voltage on or voltage off. + +```csharp +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. + +### Current Limits + +The Raspberry Pi's GPIO pins can supply a maximum of about **16 mA per pin**. An LED with a 220 Ω resistor draws about 6 mA — well within this limit. + +For higher-power devices (motors, relays, strips of LEDs) you need to 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. + +## Digital Input: Reading Buttons and Sensors + +To read whether a pin is receiving a high or low signal, open it in an input mode: + +```csharp +using GpioController controller = new(); + +controller.OpenPin(17, PinMode.InputPullUp); + +PinValue value = controller.Read(17); + +if (value == PinValue.Low) +{ + Console.WriteLine("Button is pressed!"); +} +``` + +### Why InputPullUp Instead of Input? + +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. + +### 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 it is released, the internal pull-up resistor pulls it back to High. + +## Reacting to Changes with 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; + +const int buttonPin = 17; + +using GpioController controller = new(); + +controller.OpenPin(buttonPin, PinMode.InputPullUp); + +controller.RegisterCallbackForPinValueChangedEvent( + buttonPin, + PinEventTypes.Falling, // trigger on High → Low (button press) + (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. For reliable button handling, see the debouncing techniques described in [PR #2438's debouncing guide](https://github.com/dotnet/iot/pull/2438). + +## 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 (because of InputPullUp) + PinValue buttonState = controller.Read(buttonPin); + controller.Write(ledPin, buttonState == PinValue.Low ? PinValue.High : PinValue.Low); + Thread.Sleep(50); +} +``` + +## GPIO Drivers: How .NET Talks to Hardware + +Underneath, `GpioController` uses a **driver** to communicate with the operating system's GPIO interface. 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(); +``` + +The auto-detection works like this: + +| Board | Driver selected | +| --- | --- | +| Raspberry Pi 3 / 4 | `RaspberryPi3Driver` | +| Raspberry Pi 5 | `LibGpiodDriver` (with correct chip) | +| Other Linux boards | Tries `LibGpiodDriver` → `LibGpiodV2Driver` → `SysFsDriver` | + +### When to Choose a Driver Manually + +You only need to specify a driver explicitly if: + +- Auto-detection picks the wrong driver for your board +- You need to target a specific GPIO chip number +- You are troubleshooting GPIO access issues + +```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)); +``` + +### 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 — auto-selected, legacy direct memory access | +| `SysFsDriver` | Very old Linux kernels (pre-4.8) only — **deprecated** | + +### 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). + +## Raspberry Pi 5 Note + +Raspberry Pi 5 moved its GPIO to a different chip number. If you are explicitly constructing a driver, use chip **4** instead of 0: + +```csharp +// Raspberry Pi 5 only +using GpioController controller = new(new LibGpiodDriver(gpioChip: 4)); +``` + +The parameterless `new GpioController()` constructor handles this automatically on Pi 5, so you only need to worry about this when creating a driver manually. + +To find the correct chip number on any board: + +```bash +gpioinfo +``` + +## Troubleshooting + +### "Permission denied" when accessing GPIO + +Add your user to the `gpio` group, then **log out and back in**: + +```bash +sudo usermod -aG gpio $USER +``` + +### "libgpiod not found" / DllNotFoundException + +Install the library: + +```bash +sudo apt install libgpiod2 +``` + +### GPIO operations have no effect / wrong pin + +Make sure you are using **GPIO (BCM) 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/). + +### "Pin is already in use" + +This can happen if: + +1. Another process is using the pin — stop it or reboot +2. A previous run did not dispose the controller properly — always use `using` statements +3. The pin is reserved by a kernel driver (I2C, SPI, UART) — choose a different pin + +## Best Practices + +1. **Always use `using` statements** — this ensures pins are released when your program exits +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** — calculate the value or use 220 Ω as a safe default +5. **Check pin assignments** — some GPIO pins are shared with I2C, SPI, or UART; using them for general GPIO disables those interfaces + +## Voltage Safety + +Raspberry Pi GPIO pins operate at **3.3 V**. Connecting a 5 V signal directly to a GPIO input **will permanently damage the pin**. If you need to interface with 5 V devices, use a level shifter or a voltage divider. + +## Next Steps + +- Browse the [130+ device bindings](../src/devices/README.md) for sensors, displays, motors, and more +- Read the [libgpiod guide](gpio-linux-libgpiod.md) for advanced driver configuration +- Check the [Raspberry Pi I2C setup](raspi-i2c.md), [SPI setup](raspi-spi.md), and [PWM setup](raspi-pwm.md) for other communication protocols +- Visit [pinout.xyz](https://pinout.xyz/) for an interactive Raspberry Pi pin reference diff --git a/Documentation/gpio-linux-libgpiod.md b/Documentation/gpio-linux-libgpiod.md index bede52a583..5bdc63ee0d 100644 --- a/Documentation/gpio-linux-libgpiod.md +++ b/Documentation/gpio-linux-libgpiod.md @@ -2,27 +2,30 @@ ## 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 +using System.Device.Gpio; +using System.Device.Gpio.Drivers; + +// On the Raspberry Pi the GPIO chip line offsets match BCM GPIO numbering const int ledGpio = 15; -// 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 Pi5 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 +47,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)); + +// For libgpiod v2 +using var controller = new GpioController(new LibGpiodV2Driver(chipNumber: 0)); +``` -When not explicitly specified, dotnet iot automatically tries to find a driver compatible to what library version is installed. +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 From ab3104e38e0898c6f44ac59765f571e3bcc3d2e0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 15:44:12 +0000 Subject: [PATCH 2/7] Address review feedback: improve debouncing note and clarify inverted button logic Agent-Logs-Url: https://github.com/dotnet/iot/sessions/125709c9-1313-48ad-8388-a71a2989a941 Co-authored-by: krwq <660048+krwq@users.noreply.github.com> --- Documentation/gpio-beginners-guide.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Documentation/gpio-beginners-guide.md b/Documentation/gpio-beginners-guide.md index ad3d36431f..193183b5ae 100644 --- a/Documentation/gpio-beginners-guide.md +++ b/Documentation/gpio-beginners-guide.md @@ -176,7 +176,7 @@ Thread.Sleep(Timeout.Infinite); - `PinEventTypes.Falling` — High → Low transition - `PinEventTypes.Rising | PinEventTypes.Falling` — both directions -> **Note:** Physical buttons "bounce" — a single press can generate several rapid transitions. For reliable button handling, see the debouncing techniques described in [PR #2438's debouncing guide](https://github.com/dotnet/iot/pull/2438). +> **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 @@ -199,7 +199,8 @@ Console.WriteLine("Hold the button to light the LED. Ctrl+C to exit."); while (true) { - // Button pressed = Low (because of InputPullUp) + // 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); From 49cc9bfef9fb8ab135c25b91a10c364470eb78f2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 9 Apr 2026 15:55:45 +0000 Subject: [PATCH 3/7] Restructure GPIO docs to preserve paths from PR #2438 (protocols/gpio.md, fundamentals/gpio-basics.md, fundamentals/choosing-drivers.md) Agent-Logs-Url: https://github.com/dotnet/iot/sessions/c24b48b9-932c-4ad1-a39d-b2fca64a84a2 Co-authored-by: krwq <660048+krwq@users.noreply.github.com> --- Documentation/README.md | 6 +- .../fundamentals/choosing-drivers.md | 168 +++++++++ Documentation/fundamentals/gpio-basics.md | 200 +++++++++++ Documentation/gpio-beginners-guide.md | 327 ------------------ Documentation/protocols/gpio.md | 292 ++++++++++++++++ 5 files changed, 664 insertions(+), 329 deletions(-) create mode 100644 Documentation/fundamentals/choosing-drivers.md create mode 100644 Documentation/fundamentals/gpio-basics.md delete mode 100644 Documentation/gpio-beginners-guide.md create mode 100644 Documentation/protocols/gpio.md diff --git a/Documentation/README.md b/Documentation/README.md index f325ada9e0..6bac1bc00a 100644 --- a/Documentation/README.md +++ b/Documentation/README.md @@ -31,8 +31,10 @@ While contributing, you should read the [coding guidelines section](https://gith ### General-Purpose Input/Output (GPIO) -* [GPIO Guide for Beginners](./gpio-beginners-guide.md) - Getting started with GPIO using .NET IoT -* [Using libgpiod to control GPIOs](./gpio-linux-libgpiod.md) - Advanced libgpiod driver configuration +* [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..ce85f415d7 --- /dev/null +++ b/Documentation/fundamentals/choosing-drivers.md @@ -0,0 +1,168 @@ +# 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** | Automatic | Manual | +| **Future Support** | Yes | No (being removed) | + +## Auto-Detection + +When you use `new GpioController()`, the framework automatically selects the best driver: + +| Board | Driver selected | +| --- | --- | +| Raspberry Pi 3 / 4 | `RaspberryPi3Driver` | +| Raspberry Pi 5 | `LibGpiodDriver` (with correct GPIO chip) | +| 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:** chip number **4** (`gpiochip4`) + +## 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) + +Raspberry Pi 5 uses chip **4** instead of 0. If auto-detection does not work: + +```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..5c06ff12ad --- /dev/null +++ b/Documentation/fundamentals/gpio-basics.md @@ -0,0 +1,200 @@ +# 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 Pi. A 220 Ω resistor limits the current to a safe level (~6 mA). + +## 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.Device.Gpio; + +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!) +- **Max current per pin:** ~16 mA (safe limit) +- **Max total current (all pins):** ~50 mA +- **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 16 mA (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 18, 19:** 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** — Calculate the value or use 220 Ω as a safe default +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-beginners-guide.md b/Documentation/gpio-beginners-guide.md deleted file mode 100644 index 193183b5ae..0000000000 --- a/Documentation/gpio-beginners-guide.md +++ /dev/null @@ -1,327 +0,0 @@ -# GPIO Guide for Beginners - -General-Purpose Input/Output (GPIO) pins are programmable electrical pins on single-board computers like the Raspberry Pi. They let your .NET code interact with physical hardware — turning LEDs on and off, reading button presses, and communicating with sensors. - -This guide will walk you through everything you need to know to get started with GPIO using the .NET IoT library. - -## What You Need - -- 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**, a **220 Ω resistor**, and a **push button** - -> **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 - -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 LED -2. Short leg (cathode, −) of LED → one end of **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. - -## Key Concept: 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: Controlling Devices - -Setting a pin to **High** sends 3.3 V to the pin. Setting it to **Low** sends 0 V. That is all digital output does: voltage on or voltage off. - -```csharp -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. - -### Current Limits - -The Raspberry Pi's GPIO pins can supply a maximum of about **16 mA per pin**. An LED with a 220 Ω resistor draws about 6 mA — well within this limit. - -For higher-power devices (motors, relays, strips of LEDs) you need to 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. - -## Digital Input: Reading Buttons and Sensors - -To read whether a pin is receiving a high or low signal, open it in an input mode: - -```csharp -using GpioController controller = new(); - -controller.OpenPin(17, PinMode.InputPullUp); - -PinValue value = controller.Read(17); - -if (value == PinValue.Low) -{ - Console.WriteLine("Button is pressed!"); -} -``` - -### Why InputPullUp Instead of Input? - -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. - -### 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 it is released, the internal pull-up resistor pulls it back to High. - -## Reacting to Changes with 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; - -const int buttonPin = 17; - -using GpioController controller = new(); - -controller.OpenPin(buttonPin, PinMode.InputPullUp); - -controller.RegisterCallbackForPinValueChangedEvent( - buttonPin, - PinEventTypes.Falling, // trigger on High → Low (button press) - (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. - -## 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); -} -``` - -## GPIO Drivers: How .NET Talks to Hardware - -Underneath, `GpioController` uses a **driver** to communicate with the operating system's GPIO interface. 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(); -``` - -The auto-detection works like this: - -| Board | Driver selected | -| --- | --- | -| Raspberry Pi 3 / 4 | `RaspberryPi3Driver` | -| Raspberry Pi 5 | `LibGpiodDriver` (with correct chip) | -| Other Linux boards | Tries `LibGpiodDriver` → `LibGpiodV2Driver` → `SysFsDriver` | - -### When to Choose a Driver Manually - -You only need to specify a driver explicitly if: - -- Auto-detection picks the wrong driver for your board -- You need to target a specific GPIO chip number -- You are troubleshooting GPIO access issues - -```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)); -``` - -### 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 — auto-selected, legacy direct memory access | -| `SysFsDriver` | Very old Linux kernels (pre-4.8) only — **deprecated** | - -### 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). - -## Raspberry Pi 5 Note - -Raspberry Pi 5 moved its GPIO to a different chip number. If you are explicitly constructing a driver, use chip **4** instead of 0: - -```csharp -// Raspberry Pi 5 only -using GpioController controller = new(new LibGpiodDriver(gpioChip: 4)); -``` - -The parameterless `new GpioController()` constructor handles this automatically on Pi 5, so you only need to worry about this when creating a driver manually. - -To find the correct chip number on any board: - -```bash -gpioinfo -``` - -## Troubleshooting - -### "Permission denied" when accessing GPIO - -Add your user to the `gpio` group, then **log out and back in**: - -```bash -sudo usermod -aG gpio $USER -``` - -### "libgpiod not found" / DllNotFoundException - -Install the library: - -```bash -sudo apt install libgpiod2 -``` - -### GPIO operations have no effect / wrong pin - -Make sure you are using **GPIO (BCM) 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/). - -### "Pin is already in use" - -This can happen if: - -1. Another process is using the pin — stop it or reboot -2. A previous run did not dispose the controller properly — always use `using` statements -3. The pin is reserved by a kernel driver (I2C, SPI, UART) — choose a different pin - -## Best Practices - -1. **Always use `using` statements** — this ensures pins are released when your program exits -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** — calculate the value or use 220 Ω as a safe default -5. **Check pin assignments** — some GPIO pins are shared with I2C, SPI, or UART; using them for general GPIO disables those interfaces - -## Voltage Safety - -Raspberry Pi GPIO pins operate at **3.3 V**. Connecting a 5 V signal directly to a GPIO input **will permanently damage the pin**. If you need to interface with 5 V devices, use a level shifter or a voltage divider. - -## Next Steps - -- Browse the [130+ device bindings](../src/devices/README.md) for sensors, displays, motors, and more -- Read the [libgpiod guide](gpio-linux-libgpiod.md) for advanced driver configuration -- Check the [Raspberry Pi I2C setup](raspi-i2c.md), [SPI setup](raspi-spi.md), and [PWM setup](raspi-pwm.md) for other communication protocols -- Visit [pinout.xyz](https://pinout.xyz/) for an interactive Raspberry Pi pin reference diff --git a/Documentation/protocols/gpio.md b/Documentation/protocols/gpio.md new file mode 100644 index 0000000000..ee7fc97396 --- /dev/null +++ b/Documentation/protocols/gpio.md @@ -0,0 +1,292 @@ +# 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. + +## What You Need + +- 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 + +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/). + +## 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); +``` + +**Best practice:** Always use `InputPullUp` or `InputPullDown` for input pins. Plain `Input` mode leaves the pin "floating" (undefined voltage), causing unreliable readings. See [GPIO Basics](../fundamentals/gpio-basics.md) for a detailed explanation of pull-up and pull-down resistors. + +## 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 +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 — auto-selected, legacy direct memory access | +| `SysFsDriver` | Very old Linux kernels (pre-4.8) only — **deprecated** | + +### Auto-Detection + +When you use `new GpioController()`, the framework selects automatically: + +- **Raspberry Pi 3/4:** `RaspberryPi3Driver` +- **Raspberry Pi 5:** `LibGpiodDriver` (with correct GPIO chip) +- **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). + +## Raspberry Pi 5 Note + +Raspberry Pi 5 moved its GPIO to a different chip number. If you are explicitly constructing a driver, use chip **4** instead of 0: + +```csharp +// Raspberry Pi 5 only +using GpioController controller = new(new LibGpiodDriver(gpioChip: 4)); +``` + +The parameterless `new GpioController()` constructor handles this automatically on Pi 5, so you only need to worry about this when creating a driver manually. + +To find the correct chip number on any board: + +```bash +gpioinfo +``` + +## 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/). + +## 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) From 7f680f75dd10f9d220a3c1a3b7b2f0397855db2c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:49:26 +0000 Subject: [PATCH 4/7] Address review feedback: broaden board requirements, add input introduction, add LED current notes Agent-Logs-Url: https://github.com/dotnet/iot/sessions/c7565b37-f74a-44ad-8f38-5993f685f665 Co-authored-by: raffaeler <5183152+raffaeler@users.noreply.github.com> --- Documentation/fundamentals/gpio-basics.md | 4 +-- Documentation/protocols/gpio.md | 35 ++++++++++++++++++++--- 2 files changed, 33 insertions(+), 6 deletions(-) diff --git a/Documentation/fundamentals/gpio-basics.md b/Documentation/fundamentals/gpio-basics.md index 5c06ff12ad..dd2233c18b 100644 --- a/Documentation/fundamentals/gpio-basics.md +++ b/Documentation/fundamentals/gpio-basics.md @@ -55,7 +55,7 @@ LEDs require a current-limiting resistor to prevent damage: 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 Pi. A 220 Ω resistor limits the current to a safe level (~6 mA). +> **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 @@ -184,7 +184,7 @@ Not all GPIO pins are suitable for general use: 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** — Calculate the value or use 220 Ω as a safe default +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 diff --git a/Documentation/protocols/gpio.md b/Documentation/protocols/gpio.md index ee7fc97396..1a029e3da0 100644 --- a/Documentation/protocols/gpio.md +++ b/Documentation/protocols/gpio.md @@ -4,7 +4,7 @@ General-Purpose Input/Output (GPIO) pins are programmable pins on your single-bo ## What You Need -- A **Raspberry Pi** (3, 4, or 5) running Raspberry Pi OS +- 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** @@ -29,7 +29,7 @@ using System; using System.Device.Gpio; using System.Threading; -const int ledPin = 18; // GPIO 18 = physical pin 12 on Raspberry Pi +const int ledPin = 18; // GPIO 18 = physical pin 12 on Raspberry Pi (see https://pinout.xyz/) using GpioController controller = new(); @@ -74,6 +74,35 @@ You should see the LED blink once per second. 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: @@ -89,8 +118,6 @@ controller.OpenPin(17, PinMode.InputPullUp); controller.OpenPin(17, PinMode.InputPullDown); ``` -**Best practice:** Always use `InputPullUp` or `InputPullDown` for input pins. Plain `Input` mode leaves the pin "floating" (undefined voltage), causing unreliable readings. See [GPIO Basics](../fundamentals/gpio-basics.md) for a detailed explanation of pull-up and pull-down resistors. - ## Reading and Writing ```csharp From b1791c7e97c702d9e83df979bfc5181bd83b1dfa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 16:02:24 +0000 Subject: [PATCH 5/7] Add table of contents, improve driver descriptions, move Pi 5 chip issue to troubleshooting Agent-Logs-Url: https://github.com/dotnet/iot/sessions/150e5c2f-9425-4ea1-8bb6-d2655685087e Co-authored-by: krwq <660048+krwq@users.noreply.github.com> --- Documentation/protocols/gpio.md | 61 +++++++++++++++++++++++---------- 1 file changed, 42 insertions(+), 19 deletions(-) diff --git a/Documentation/protocols/gpio.md b/Documentation/protocols/gpio.md index 1a029e3da0..0f2b2e625c 100644 --- a/Documentation/protocols/gpio.md +++ b/Documentation/protocols/gpio.md @@ -2,6 +2,22 @@ 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) @@ -202,8 +218,8 @@ using GpioController controller = new(); | --- | --- | | `LibGpiodDriver` | Modern Linux with libgpiod v1 — the recommended default | | `LibGpiodV2Driver` | Modern Linux with libgpiod v2 | -| `RaspberryPi3Driver` | Raspberry Pi 3/4 — auto-selected, legacy direct memory access | -| `SysFsDriver` | Very old Linux kernels (pre-4.8) only — **deprecated** | +| `RaspberryPi3Driver` | Raspberry Pi 3/4 — very fast (direct memory access) but requires root permissions; not recommended unless you specifically need the performance | +| `SysFsDriver` | Very slow but works on any Linux version — **deprecated** | ### Auto-Detection @@ -238,23 +254,6 @@ sudo apt install libgpiod2 For more detail on libgpiod versions and building from source, see [Using libgpiod to control GPIOs](../gpio-linux-libgpiod.md). -## Raspberry Pi 5 Note - -Raspberry Pi 5 moved its GPIO to a different chip number. If you are explicitly constructing a driver, use chip **4** instead of 0: - -```csharp -// Raspberry Pi 5 only -using GpioController controller = new(new LibGpiodDriver(gpioChip: 4)); -``` - -The parameterless `new GpioController()` constructor handles this automatically on Pi 5, so you only need to worry about this when creating a driver manually. - -To find the correct chip number on any board: - -```bash -gpioinfo -``` - ## Troubleshooting ### "Permission denied" Error @@ -297,6 +296,30 @@ System.InvalidOperationException: Pin 18 is already in use 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 From 0fc38b1717267831f82fef639577de02aaf08159 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 16:03:42 +0000 Subject: [PATCH 6/7] Clarify driver table descriptions per code review feedback Agent-Logs-Url: https://github.com/dotnet/iot/sessions/150e5c2f-9425-4ea1-8bb6-d2655685087e Co-authored-by: krwq <660048+krwq@users.noreply.github.com> --- Documentation/protocols/gpio.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/protocols/gpio.md b/Documentation/protocols/gpio.md index 0f2b2e625c..d02729d390 100644 --- a/Documentation/protocols/gpio.md +++ b/Documentation/protocols/gpio.md @@ -218,8 +218,8 @@ using GpioController controller = new(); | --- | --- | | `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) but requires root permissions; not recommended unless you specifically need the performance | -| `SysFsDriver` | Very slow but works on any Linux version — **deprecated** | +| `RaspberryPi3Driver` | Raspberry Pi 3/4 — very fast (direct memory access) but requires root permissions; auto-selected on Pi 3/4 but consider using `LibGpiodDriver` when root access is not available | +| `SysFsDriver` | Works on any Linux version but very slow — **deprecated** in favor of libgpiod-based drivers | ### Auto-Detection From 1272c28f1844924dc94ee0c3e10e78d730575478 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Thu, 21 May 2026 17:59:40 +0200 Subject: [PATCH 7/7] AI review feedback --- Documentation/fundamentals/choosing-drivers.md | 13 +++++++------ Documentation/fundamentals/gpio-basics.md | 10 ++++++---- Documentation/gpio-linux-libgpiod.md | 11 +++++++++-- Documentation/protocols/gpio.md | 13 ++++++++++--- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/Documentation/fundamentals/choosing-drivers.md b/Documentation/fundamentals/choosing-drivers.md index ce85f415d7..87b2db9d55 100644 --- a/Documentation/fundamentals/choosing-drivers.md +++ b/Documentation/fundamentals/choosing-drivers.md @@ -51,8 +51,8 @@ Uses the older `/sys/class/gpio` interface, which is deprecated in modern Linux | **Kernel Version** | 4.8+ | All | | **Performance** | Better | Good | | **Multi-process** | Safe | Unsafe | -| **Resource Cleanup** | Automatic | Manual | -| **Future Support** | Yes | No (being removed) | +| **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 @@ -60,8 +60,8 @@ When you use `new GpioController()`, the framework automatically selects the bes | Board | Driver selected | | --- | --- | -| Raspberry Pi 3 / 4 | `RaspberryPi3Driver` | -| Raspberry Pi 5 | `LibGpiodDriver` (with correct GPIO chip) | +| 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 @@ -93,7 +93,8 @@ gpioinfo ``` - **Raspberry Pi 3/4:** chip number **0** (`gpiochip0`) -- **Raspberry Pi 5:** chip number **4** (`gpiochip4`) +- **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 @@ -134,7 +135,7 @@ sudo apt install libgpiod2 ### Wrong Chip Number (Raspberry Pi 5) -Raspberry Pi 5 uses chip **4** instead of 0. If auto-detection does not work: +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)); diff --git a/Documentation/fundamentals/gpio-basics.md b/Documentation/fundamentals/gpio-basics.md index dd2233c18b..c51d796eaf 100644 --- a/Documentation/fundamentals/gpio-basics.md +++ b/Documentation/fundamentals/gpio-basics.md @@ -112,7 +112,9 @@ When the button is pressed, GPIO 17 gets connected to Ground (Low). When release 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); @@ -142,8 +144,8 @@ Thread.Sleep(Timeout.Infinite); ### Raspberry Pi GPIO Specifications - **Output Voltage:** 3.3 V (not 5 V!) -- **Max current per pin:** ~16 mA (safe limit) -- **Max total current (all pins):** ~50 mA +- **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 @@ -156,7 +158,7 @@ Thread.Sleep(Timeout.Infinite); ### Driving High-Current Devices -For loads exceeding 16 mA (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. +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 @@ -174,7 +176,7 @@ 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 18, 19:** PWM-capable +- **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/). diff --git a/Documentation/gpio-linux-libgpiod.md b/Documentation/gpio-linux-libgpiod.md index 5bdc63ee0d..7bd8faee1d 100644 --- a/Documentation/gpio-linux-libgpiod.md +++ b/Documentation/gpio-linux-libgpiod.md @@ -7,15 +7,16 @@ This example targets a Raspberry Pi 3/4, see comments for more information: ```c# 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 = 15; +const int ledGpio = 18; // '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(); -// To explicitly use LibGpiodDriver (e.g. on Pi5 where chip 4 is needed): +// 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)); controller.OpenPin(ledGpio, PinMode.Output); @@ -118,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 index d02729d390..f9dbe5075c 100644 --- a/Documentation/protocols/gpio.md +++ b/Documentation/protocols/gpio.md @@ -154,6 +154,13 @@ if (value == PinValue.Low) 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) @@ -218,15 +225,15 @@ using GpioController controller = new(); | --- | --- | | `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) but requires root permissions; auto-selected on Pi 3/4 but consider using `LibGpiodDriver` when root access is not available | +| `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` -- **Raspberry Pi 5:** `LibGpiodDriver` (with correct GPIO chip) +- **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).