GPIO Test Application Part 3 – Model

Previous: GPIO Test Application Part 2 – Hardware

This post is part 3 of 6 of the GPIO Test Application. You can find the initial post here: GPIO Test Application part 1 – Overview.

The model of our application will provide the data used by the application. The data in this case represents the information that is sent and received on the GPOI.

Model Application Interface

The Model exposes a simple and easy to use interface to the application. This interface hides all the complexity of working with the GPIO. The properties for the interface look like regular content properties of an INotifyPropertyChange object.

The following properties compose this interface:

  • RightSwitch: bool value that returns true when the right switch is pressed.
  • LeftSwitch: bool value that returns true when the left switch is pressed.
  • RedLED: bool value that represents if the red LED is on / off. Setting this value will turn the green LED on.
  • GreenLED: bool value that represents if the green LED is on / off. Setting this value will turn the green LED on.
  • IsEnabled: read only bool value that specify if the GPIOs have been successfully initialized and are available. This property is used to enter simulation mode. In simulation mode, the application adds buttons on the screen to simulate the left and right push buttons on the screen. This is useful for debugging the application on your computer.

The switch properties looks like this:

private bool m_RightSwitch = true;

/// <summary>
/// Provides access to the value of the Right push switch on the hardware
/// </summary>
public bool RightSwitch
{
    get
    {
        return m_RightSwitch;
    }
    set
    {
        if (value != m_RightSwitch)
        {
            m_RightSwitch = value;
            OnPropertyChanged();
        }
    }
}

The LED properties looks like this:

private bool m_RedLED = false;

/// <summary>
/// Reads and Sets the value of the red LED
/// </summary>
public bool RedLED
{
    get
    {
        return m_RedLED;
    }
    set
    {
        if (value != m_RedLED)
        {
            m_RedLED = value;
            UpdateGPIO(m_RedLEDPin, value);
            OnPropertyChanged();
        }
    }
}

Notice that in the setter of the LED properties, a call is made to UpdateGPIO to send the value to the GPIO.

GPIO processing

In the case of our application, the Model is encapsulating all the GPIO access. The flow of GPIO runs something like this:

  1. Initialize the GPIO
  2. While the application is running:
    1. Wait for events on the Input pins
    2. Write Outputs to the Output pins
  3. Shut down the GPIO

We keep the instance of the GpioPin for each pin that we use. The GpioPin are created in the initialization process and released in the dispose. For simplicity, we keep the pin numbers in constants fields.

private const int GPIO_LEFT_SWITCH = 21;
private const int GPIO_RIGHT_SWITCH = 20;
private const int GPIO_RED_LED = 6;
private const int GPIO_GREEN_LED = 5;

private GpioController m_Controller = null;
private GpioPin m_LeftSwitchPin = null;
private GpioPin m_RightSwitchPin = null;
private GpioPin m_RedLEDPin = null;
private GpioPin m_GreenLEDPin = null;

Initializing / Shutting down the GPIO

During the initialization process the following sequence occurs:

  1. The application attempts to retrieve the default GpioController. If the controller cannot be retrieved, this means that the device does not support GPIO.
  2. All the pins are initialized.
  3. If all the pins are initialized correctly, the IsEnabled property is set to true.
  4. If one of the pins fails to initialize, the Release() function is called to free up all the allocated resources.
private void Initialize()
{
    Logger.AddLogEntry("Initializing GPIO on LogicGateModel");

    m_Controller = GpioController.GetDefault();
    if (m_Controller != null)
    {
        string startupMessage = "Startup:\r\n";
        bool success = true;

        success &= InitializeLEDPin(GPIO_GREEN_LED, "green LED", out m_GreenLEDPin, ref startupMessage);
        success &= InitializeLEDPin(GPIO_RED_LED, "red LED", out m_RedLEDPin, ref startupMessage);

        success &= InitializeSwitchPin(GPIO_LEFT_SWITCH, "left switch", out m_LeftSwitchPin, ref startupMessage);
        success &= InitializeSwitchPin(GPIO_RIGHT_SWITCH, "right switch", out m_RightSwitchPin, ref startupMessage);

        Message = startupMessage;
        if (!success)
        {
            Release();
        }
        else
        {
            // Read the current values...
            LeftSwitch = m_LeftSwitchPin.Read() == GpioPinValue.High;
            RightSwitch = m_RightSwitchPin.Read() == GpioPinValue.High;

            IsEnabled = true;
        }
    }
    else
    {
        Message = "Unable to access the GpioController!";
    }

    Logger.AddLogEntry("Initialization of GPIO on LogicGateModel complete!");
}

When the LED pins are created:

  1. We use the controller to try to open up the pin
  2. If that succeeds, we make sure the pins supports the Output Drive Mode.
  3. Then we set the Drive Mode to Output
  4. If any of the operations do not succeed, we set the result to false to indicate that there was an error initializing the GPIO
private bool InitializeLEDPin(int pin, string pinName, out GpioPin output, ref string message)
{
    bool result = true;

    Logger.AddLogEntry("Initializing Pin #{0}, {1}", pin, pinName);

    GpioOpenStatus status;
    if (m_Controller.TryOpenPin(pin, GpioSharingMode.Exclusive, out output, out status))
    {
        if (output.IsDriveModeSupported(GpioPinDriveMode.Output))
        {
            output.SetDriveMode(GpioPinDriveMode.Output);
            message += string.Format("   Pin {0} initialized: status = {1}\r\n", pinName, status.ToString());
        }
        else
        {
            message += string.Format("   Pin {0} error: Drive mode {1} not supported.\r\n", pinName, status.ToString());
            result = false;
        }
    }
    else
    {
        message += string.Format("   Can't initialize {0}: status = {1}\r\n", pinName, status.ToString());
        result = false;
    }

    return result;
}

When the switch pins are created:

  1. We use the controller to try to open up the pin
  2. If that succeeds, we make sure the pins supports the Output Drive Mode.
  3. Then we set the Drive Mode to Output
  4. Then we attach an event handler to the ValueChanged event of the GpioPinto  to receive notification whenever there is a change of state on the pin.
  5. If any of the operations do not succeed, we set the result to false to indicate that there was an error initializing the GPIO
private bool InitializeSwitchPin(int pin, string pinName, out GpioPin output, ref string message)
{
    bool result = true;

    Logger.AddLogEntry("Initializing Pin #{0}, {1}", pin, pinName);

    GpioOpenStatus status;
    if (m_Controller.TryOpenPin(pin, GpioSharingMode.Exclusive, out output, out status))
    {
        if (output.IsDriveModeSupported(GpioPinDriveMode.Input))
        {
            output.SetDriveMode(GpioPinDriveMode.Input);

            //Intentional, we do not use debounce this time but this is where you would set it
            //output.DebounceTimeout = TimeSpan.FromMilliseconds(5);

            output.ValueChanged += PinValueChanged;
            message += string.Format("   Pin {0} initialized: status = {1}\r\n", pinName, status.ToString());
        }
        else
        {
            message += string.Format("   Pin {0} error: Drive mode {1} not supported.\r\n", pinName, status.ToString());
            result = false;
        }
    }
    else
    {
        message += string.Format("   Can't initialize {0}: status = {1}\r\n", pinName, status.ToString());
        result = false;
    }

    return result;
}

The Release function is strait forward, it disconnects the event handler from the input pins and calls Dispose on each of the pins.

private void Release()
{
    Logger.AddLogEntry("Releasing GPIO on LogicGateModel");

    if (m_Controller != null)
    {
        // Release all the GPIO pins used
        if (m_LeftSwitchPin != null)
        {
            m_LeftSwitchPin.ValueChanged -= PinValueChanged;
            m_LeftSwitchPin.Dispose();
        }

        if (m_RightSwitchPin != null)
        {
            m_RightSwitchPin.ValueChanged -= PinValueChanged;
            m_RightSwitchPin.Dispose();
        }

        if (m_GreenLEDPin != null)
        {
            m_GreenLEDPin.Dispose();
        }

        if (m_RedLEDPin != null)
        {
            m_RedLEDPin.Dispose();
        }
    }
    Logger.AddLogEntry("Completed Releasing GPIO on LogicGateModel");
}

Handling OnChange event on the input PINs

When initializing the pins we assigned an event handler to the ValueChanged event of the input pins. The following code shows the OnValueChanged function:

private void PinValueChanged(GpioPin sender, GpioPinValueChangedEventArgs args)
{
    // normally we would use the arg to get the value, as follow:
    //    (args.Edge == GpioPinEdge.FallingEdge);
    // but at this point it is completely unreliable and returns garbage
    bool value = sender.Read() == GpioPinValue.High;

    if (sender == m_LeftSwitchPin)
    {
        CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
        {
            LeftSwitch = value;
        });
    }
    else if (sender == m_RightSwitchPin)
    {
        CoreApplication.MainView.Dispatcher.RunAsync(CoreDispatcherPriority.High, () =>
        {
            RightSwitch = value;
        });
    }
}

We first read the value of the pin by calling the Read() function. The GpioPin that raised the event is received as the sender parameter, so we use it to identify which button was pressed. Then we just set the value on the appropriate property.

One particularity of this code is that the ValueChange event is raised on a separate thread. In order to interact with the User Interface, we need to bring the call back on the UI thread. That is done here with the CoreApplication.MainView.Dispatcher.RunAsync call.

Turning LEDs On/Off

When the ViewModel sets the value of the RedLED or GreenLED, we need to send that value GPIO, the UpdateGPOI function does that for us:

private void UpdateGPIO(GpioPin pin, bool value)
{
    if (IsEnabled && pin != null)
    {
        GpioPinValue v = value ? GpioPinValue.Low : GpioPinValue.High;
        pin.Write(v);
    }
}

Conclusion

The Model already provides a big portion of the overall functionality we require. It reads inputs from the GPIO, maintains the current state of Input/Outputs and provides a way to turn LEDs on and off. All that using a simple interface that the rest of the application can use.

Next: GPIO Test Application Part 4 – ViewModel

, , , ,

  1. Leave a comment

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: