Skip to content

Instantly share code, notes, and snippets.

@JohnTasler
Last active October 20, 2018 02:13
Show Gist options
  • Save JohnTasler/ea4ae4daa7bba31f7021942855ac759b to your computer and use it in GitHub Desktop.
Save JohnTasler/ea4ae4daa7bba31f7021942855ac759b to your computer and use it in GitHub Desktop.
Api Proposal for System.Device.Gpio Round 3

System.Device.LowLevel Proposal

The goal of this API is to allow .NET Core developers to access low-level hardware controllers on IoT devices such as Raspberry Pi, Hummingboard, Odroid, DragonBoard, etc. This will allow applications to read/write data from/to sensors, LED's, motors, and other kinds of peripherals connected to such a device. This API should support the most common low-level device protocols, including General Purpose IO (GPIO) pins, serial communication protocols like SPI and I2C, Pulse Width Modulation (PWM), and Analog-to-Digital Converters (ADC).

Namespaces

(NOTE: italicsized namespace are not yet addressed in this document.)

  • System.Device.Gpio: Will contain the types that allow access to the GPIO pins in order to opening and closing pins, reading and writing values to them, and subscribing for event notifications.
  • System.Device.Pwm : Will contain the types that allow access to PWM (Pulse Width Modulation). This will allow developers to send square waves over a pin in order to simulate analog values from digital outputs.
  • System.Device.I2c : Will contain the types that allow developers to communicate with devices that use the I2C protocol to transfer data.
  • System.Device.Spi : Will contain the types that allow developers to communicate with devices that use the SPI protocol to transfer data.
  • System.Device.Adc : Will contain the types that allow developers to communicate with analog-to-digital converter devices.

Changes vs Iteration 2

Iteration 1 proposed a pin-object based API. Iteration 2 proposed an integer-based API, with no pin object. This iteration proposes a "best of both" API. It is pin-object based, but also provides the simplicity of the integer based API. Because the pin objects are public, the disposable (lifetime) issues are moved from the controller to the pins.

Pros of a best-of-both object model

  • Allows the application or library developer the flexibility to use whichever model is best for their implementation. Library developers would be encouraged to use the pin-object model due to better performance, since it is direct to the pin implementation (no validation or dictionary lookups).
  • Begins with a pin-object model, which models the actual objects involved, as well as a more direct mapping to how each driver will implement the model, and then the pin integers can also be used as wrappers around the pin object.
  • By having the pin objects be disposable and public, the developer is only concerned about the lifetime of the pin object.
  • The lifetime of the controller singleton model goes away, since it would no longer need to be disposable.

Cons of a best-of-both object model

  • Would it really be so confusing to a developer to have this flexibility???

Comparison of Iteration 2 and this Iteration model examples

These examples do not deal with Controller lifetime (Singleton, Disposable).

Turning on a LED

Pin Object model with non-object usage (Iteration 3)
const int led = 5;
var controller = GpioController.Default;
controller[led].OpenPin(PinMode.Output);
for (int i = 0; i < 5; ++i)
{
    controller[led].Write(PinValue.High);
    Thread.Sleep(TimeSpan.FromSeconds(1));
    controller[led].Write(PinValue.Low);
    Thread.Sleep(TimeSpan.FromSeconds(1));
}
controller[led].ClosePin();
No pin object model (Iteration 2)
const int led = 5;
var controller = GpioController.Default;
controller.OpenPin(led, PinMode.Output);
for (int i = 0; i < 5; ++i)
{
    controller.Write(led, PinValue.High);
    Thread.Sleep(TimeSpan.FromSeconds(1));
    controller.Write(led, PinValue.Low);
    Thread.Sleep(TimeSpan.FromSeconds(1));
}
controller.ClosePin(led);
Pin Object model
var controller = GpioController.Default;
using (GpioPin led = controller.OpenPin(5, PinMode.Output))
{
    for (int i = 0; i < 5; ++i)
    {
        led.Write(PinValue.High);
        Thread.Sleep(TimeSpan.FromSeconds(1));
        led.Write(PinValue.Low);
        Thread.Sleep(TimeSpan.FromSeconds(1));
    }
}

Reading if a button is pressed

Pin Object model with non-object usage (Iteration 3)
const int button = 4;
var controller = GpioController.Default;
controller[button].OpenPin(PinMode.Input);
for (int i = 0; i < 5; ++i)
{
    Console.WriteLine($"Button value is: {controller[button].Read()}");
    Thread.Sleep(TimeSpan.FromSeconds(1));
}
controller[button].ClosePin();
No pin object model (Iteration 2)
const int button = 4;
var controller = GpioController.Default;
controller.OpenPin(button, PinMode.Input);
for (int i = 0; i < 5; ++i)
{
    Console.WriteLine($"Button value is: {controller.Read(button)}");
    Thread.Sleep(TimeSpan.FromSeconds(1));
}
controller.ClosePin(button);
Pin Object model
using (GpioPin button = GpioController.Default.OpenPin(4, PinMode.Input))
{
    for (int i = 0; i < 5; ++i)
    {
        Console.WriteLine($"Button value is: {button.Read()}");
        Thread.Sleep(TimeSpan.FromSeconds(1));
    }
}

Waiting for a button to be pressed

Pin Object model with non-object usage (Iteration 3)
const int button = 6;
var controller = GpioController.Default;
controller[button].OpenPin(PinMode.Input);
controller[button].DebounceTimeout = TimeSpan.FromMilliseconds(10);
WaitForEventResult result = controller[button].WaitForEvent(PinEventTypes.Falling | PinEventTypes.Rising, TimeSpan.FromSeconds(5).Milliseconds);
Console.WriteLine((result.TimedOut) ? "Timed out waiting for an event" : "Succesfully got an Event!");
controller[button].ClosePin(button, PinMode.Input);
No pin object model (Iteration 2)
const int button = 6;
var controller = GpioController.Default;
controller.OpenPin(button, PinMode.Input);
controller.SetDebounceTimeout(TimeSpan.FromMilliseconds(10));
WaitForEventResult result = controller.WaitForEvent(button, PinEventTypes.Falling | PinEventTypes.Rising, TimeSpan.FromSeconds(5).Milliseconds);
Console.WriteLine((result.TimedOut) ? "Timed out waiting for an event" : "Succesfully got an Event!");
controller.ClosePin(button, PinMode.Input);
Pin Object model
using (GpioPin button = GpioController.Default.OpenPin(button, PinMode.Input))
{
    button.DebounceTimeout = TimeSpan.FromMilliseconds(10);
    WaitForEventResult result = button.WaitForEvent(PinEventTypes.Falling | PinEventTypes.Rising, TimeSpan.FromSeconds(5).Milliseconds);
    Console.WriteLine((result.TimedOut) ? "Timed out waiting for an event" : "Succesfully got an Event!");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment