[API Proposal]: Adding GpioPin to GpioController in .NET IoT
See original GitHub issueBackground and motivation
In .NET IoT (https://github.com/dotnet/iot/) we do have a GpioController allowing operations on GPIO (referred as pin later). To open, write, read or close a pin, it is necessary to use the GpioController. We want to propose adding a GpioPin class that will make it easier to access directly a pin.
This GpioPin is already present and used in .NET nanoFramework (https://github.com/nanoframework/). We’ve been working to align as best as possible all hardware related API between .NET IoT and .NET nanoFramework. So GPIO, SPI, I2C, PWM, Serial API are aligned. Due to the platform differences, there are minor differences.
The GpioPin in .NET nanoFramework can be found here: https://github.com/nanoframework/System.Device.Gpio/blob/main/System.Device.Gpio/GpioPin.cs
Adding a GpioPin makes it easy for simple operations and a simpler concept as well for beginners. It is important to keep in mind that only the GpioController is allow to open pins and is responsible ultimately for the life of the GpioPin. You cannot open a GpioPin without the GpioController. You can dispose your GpioPin which will close it in the GpioController. And if the GpioController is disposed, the GpioPin won’t be able to operate the pin. This deisgn exist in .NET nanoFramework and is very sucessfull. It was existing before the GpioController API aligned and based on developers’ feedback has been kept.
Adding this GpioPin to the GpioController will result in a breaking change in the OpenPin function. This is not a code breaking change, it is a binary breaking change. See the risk section on this.
When using multiple GpioControllers or with specific controllers like the FT family, this can be an advantage to simplify the management of the pins. It could allow some better performance as well in those scenarios.
An active issue is open in .NET IoT which also reference other closed issues with more detailed discussions: https://github.com/dotnet/iot/issues/1671
API Proposal
namespace System.Device.Gpio;
public class GpioController : IDisposable
{
// Changing current OpenPin overloads to return a GpioPin instead of void. Binary breaking, non source breaking.
- public void OpenPin(int pinNumber) { }
+ public GpioPin OpenPin(int pinNumber) { }
- public void OpenPin(int pinNumber, PinMode mode) { }
+ public GpioPin OpenPin(int pinNumber, PinMode mode) { }
- public void OpenPin(int pinNumber, PinMode mode, PinValue initialValue) { }
+ public GpioPin OpenPin(int pinNumber, PinMode mode, PinValue initialValue) { }
}
+public sealed class GpioPin
+{
+ public int PinNumber { get; }
+ public PinMode GetPinMode() { }
+ public bool IsPinModeSupported(PinMode pinMode) { }
+ public void SetPinMode(PinMode value) { }
+ public PinValue Read() { }
+ public void Write(PinValue value) { }
+ public event PinChangeEventHandler ValueChanged;
+ public void Toggle() { }
+}
API Usage
// As always get a GpioController
const int PinNumber = 42;
GpioController ctrl = new GpioController();
// Then open a pin and get the GpioPin
GpioPin pin = ctrl.OpenPin(PinNumber, PinMode.Input);
// Then do directly the operations on the pin:
// Write operation, equivalentin to ctrl.Write(PinNumber, PinValue.High)
pin.Write(PinValue.High)
// Read operation, equivalent to ctrl.Read(PinNumber);
var val = pin.Read();
// Togle the pin value, no equivalent using the GpioController except reading and writing the opposite value
pin.Toggle();
Something we are trying to solve is when we do have extender and need a lot of pins. In the current situation, the only way to do this is to create your own controller taking the other controllers you need. So you basically have to do something like this:
// Very simplified version, just to show the inconvenient of the solution
public class MyGpioController : GpioController
{
private GpioController _controller1;
private GpioController _controller2;
private readonly ConcurrentDictionary<int, PinValue?> _openPins = new();
public MyGpioController(GpioController driver1, GpioController driver2)
{
_controller1 = driver1;
_controller2 = driver2;
}
// We say each driver has 4 pins
public new int PinCount => 8;
public new void OpenPin(int pinNumber)
{
if ((pinNumber >= 0) && (pinNumber < 4))
{
_controller1.OpenPin(pinNumber);
}
else if ((pinNumber >= 4) && (pinNumber < 8))
{
_controller2.OpenPin(pinNumber - 4);
}
}
public new void Write(int pinNumber, PinValue pinValue)
{
if ((pinNumber >= 0) && (pinNumber < 4))
{
_controller1.Write(pinNumber, pinValue);
}
else if ((pinNumber >= 4) && (pinNumber < 8))
{
_controller2.Write(pinNumber - 4, pinValue);
}
}
// All the rest of the controller will have to be implemented that way!
}
// Then you will have to instantiate your own GpioController with the 2 drivers:
GpioController ctrl = new(
new GpioController(new GpioDriver1()),
new GpioController(new GpioDriver2()));
// You usually need a list of pins
List<int> _pins = new() { 0, 1, 2, 3, 4, 5, 6, 7 };
// And then pass it to your device
var myDevice = new MyDevice(_pins, _ctrl);
With GpioPin, everything you need is just a list of GpioPins, regardless of the GpioController they are coming from:
// One controller will come from one driver
GpioController ctrl1 = new(new GpioDrive1());
GpioController ctrl2 = new(new GpioDrive2());
List<GpioPin> _lotsOfPins = new();
for (int i = 0; i< 4; i++)
{
_lotsOfPins.Add(ctrl1.Open(1));
_lotsOfPins.Add(ctrl1.Open(2));
}
// The device will take GpioPin list:
var myDevice = new MyDevice(_lotsOfPins);
Alternative Designs
The alternative design is to not introduce the GpioPin and stay as it is.
Risks
Main risk is the binary breaking change introduce by the change of the OpenPin function, now returning a GpioPin rather than being void. This change is not a code breaking change, it’s only in the case of updating the .NET IoT nuget in an application without recompiling it. We think, this scenario is very minimal, and most people are rebuilding the solution when updating nugets.
When recompiling the solution, no change is necessary, just to recompile the code as everything is source compatible.
Issue Analytics
- State:
- Created a year ago
- Comments:18 (16 by maintainers)
Top GitHub Comments
Closing this issue as the implementation has been done and merged.