-
Notifications
You must be signed in to change notification settings - Fork 630
Add GPIO beginners guide and fix incorrect API references in libgpiod docs #2493
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
Copilot
wants to merge
7
commits into
main
Choose a base branch
from
copilot/extract-gpio-documentation
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 3 commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
42862ef
Add GPIO beginners guide and fix incorrect API references in libgpiod…
Copilot ab3104e
Address review feedback: improve debouncing note and clarify inverted…
Copilot 49cc9bf
Restructure GPIO docs to preserve paths from PR #2438 (protocols/gpio…
Copilot 7f680f7
Address review feedback: broaden board requirements, add input introd…
Copilot b1791c7
Add table of contents, improve driver descriptions, move Pi 5 chip is…
Copilot 0fc38b1
Clarify driver table descriptions per code review feedback
Copilot 1272c28
AI review feedback
krwq File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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) | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.