in Guides, Programming

How to control GPIO pins on the Raspberry Pi 3 using C#

GPIO pins on the Raspberry Pi can be controlled using the sysfs interface, which is a virtual filesystem that the Linux kernel provides. In this guide, we will write a basic C# class to control available pins on the Pi through sysfs.

Understanding the sysfs interface
sysfs provides access to the GPIO pins at the path /sys/class/gpio. You can cd into this path and ls to list files in the directory. There are two special files here which are export and unexport. You write to the export file to activate a particular pin, while writing to unexport deactivates the pin. The following example activates GPIO pin 18.

You can verify that the pin is activated by listing the files in the /sys/class/gpio directory. You should see a gpio18 folder in the directory listing. After the pin has been activated, you should specify whether the pin should be an input or output pin before you can read or write values. You do this for input like so:

Or for output:

If the pin is specified as an output pin, you can write a value of either 0 (low) or 1 (high) for the pin. If a LED is connected to the pin for this example, a value of 0 will turn the LED off, while a value of 1 will turn the LED on. To specify the pin value, you can do this:

Once you are done with the pin, you can deactivate it using:

Writing the C# class
Now that we have an idea of how sysfs works, we can create a class to implement the necessary steps. The sysfs approach basically requires writing values to the file, so we can use simple file I/O operations to achieve the desired result. The full listing for the GPIO class can be found at https://gitlab.com/akinwale/NaniteIo/blob/master/Nanite/IO/GPIO.cs.

The first thing we’ll do is add the using statements for the namespaces. System.IO is required for FileStream, StreamReader and StreamWriter which are used for file I/O. System.Threading is required for the Thread class, while Nanite.Exceptions contains the custom exceptions defined for our project. We’ll also define enumerations for the GPIO direction and value, and a few constants for strings like the GPIO path and other special files. The class will be defined as static, because we do not need to create an instance of the class.

Pretty straightforward so far. The first method we’re going to define is the PinMode method, which will take the pin number and direction as parameters. This method will activate the pin and then set the direction to either in or out depending on the specified parameter value.

We build the pinPath string making use of Path.Combine(GPIOPath, string.Format("gpio{0}", pin));. If the value specified for the pin parameter is 18, pinPath will contain the string, "/sys/class/gpio/gpio18". The ClosePin method call is optional, but the idea behind this is that the pin should be deactivated first before activating. We also check if the gpio pin directory exists using if (!Directory.Exists(pinPath)) before activating to make sure we are not activating a pin that has already been activated.

After the request for pin activation, there may be a small delay which is why we have a while loop which waits until the corresponding gpio pin directory has been created before we set the pin direction. Thread.Sleep(500) makes the program wait 500 milliseconds before proceeding to the next statement. Note that this while loop is completely optional, but it acts as a safeguard against setting the pin direction before the gpio pin directory has been created by the system. One thing to take note of is if the gpio pin directory never gets created (for instance, if the pin is invalid), the loop may end up running forever. To fix this, we can set a maximum number of times the loop should run before ending the loop.

The next method is the ClosePin method which takes the pin number as a parameter. This method checks if the pin directory exists before it writes the pin number to the /sys/class/gpio/unexport file.

We create the Write method to write a value to a pin. It takes two parameters, the pin number and the value which is of the Value enumerator type with possible values Value.Low or Value.High. In this method, we make use Path.Combine to create the full path to the value file in the gpio pin directory. For pin 18, this will be "/sys/class/gpio/gpio18/value". If value for the value parameter is Value.Low, we write 0 to the file, otherwise if it’s Value.High, we write 1 to the file.

Finally, we have our Read method to read a value from a pin. It will return either Value.Low or Value.High depending on what the pin has been set to. The question mark at the end of the method return type indicates that we can return null for the method if the value retrieved is invalid.

To determine if the retrieved value is valid, we add a couple of checks in the method. The first is the int.TryParse method, which returns false if the retrieved value is not a valid integer. Then verify that the value is either 0 or 1 using if (pinValue != 0 && pinValue != 1). If it’s neither 0 nor 1, null is returned. Otherwise, the corresponding enumeration value is returned by casting the integer to GPIO.Value.

Finally, we can put this all together in a sample program. If a LED is connected to pin 18, the LED will light up when the value is set to High and turn off when the value is set to Low.

Source Code
The full code listing for the GPIO class can be obtained from https://gitlab.com/akinwale/NaniteIo/blob/master/Nanite/IO/GPIO.cs.

0

Write a Comment

Comment

    Webmentions

  1. Controlling GPIO pins on the PINE64 using C# – Nanite – PINE64 and Raspberry Pi HOWTOs, guides and resources

    […] Raspberry Pi, GPIO pins on the PINE64 can be controlled through sysfs. You can refer to my previous post which goes into the concept in detail, and the C# code remains the same for the PINE64. However, […]