FEZ Spider TinyCLR button ValueChanged event fires twice

I have a button v1.4 connected to socket 4 on my FEZ Spider running TinyCLR v0.6. I’m simply trying to turn the led on the button on and off using the ValueChanged event of the button but the event fires twice with a single button press causing the LED to turn on then off. I’m not sure if this is because the event fires when the button is down and then again when it is up. I’m checking for the FallingEdge but it seems to happen twice. I’ve tried a couple of different things which I’ve commented out.

Any help would be appreciated. Thanks.

using System;
using System.Collections;
using System.Text;
using System.Threading;
using GHIElectronics.TinyCLR.Devices.Gpio;
using GHIElectronics.TinyCLR.Pins;

namespace ButtonLedExperiment
{
class Program
{
static GpioPin led;
//static GpioPin debugLed;
static void Main()
{
led = GpioController.GetDefault().OpenPin(FEZSpider.GpioPin.Socket4.Pin4);
led.SetDriveMode(GpioPinDriveMode.Output);

        //debugLed = GpioController.GetDefault().OpenPin(FEZSpider.GpioPin.DebugLed);
        //led.SetDriveMode(GpioPinDriveMode.Output);

        var button = GpioController.GetDefault().OpenPin(FEZSpider.GpioPin.Socket4.Pin3);
        button.SetDriveMode(GpioPinDriveMode.InputPullUp);
        button.ValueChanged += Button_ValueChanged;

        Thread.Sleep(-1);
    }

    private static void Button_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs e)
    {

        //GpioPinValue value = sender.Read();

        //if (value == GpioPinValue.High)
        //{
        //    led.Write(GpioPinValue.Low);
        //}
        //else
        //{
        //    led.Write(GpioPinValue.High);
        //}

        //if (e.Edge == GpioPinEdge.FallingEdge)
        //    led.Write(GpioPinValue.Low);
        //else
        //    led.Write(GpioPinValue.High);

        if (e.Edge == GpioPinEdge.FallingEdge)
        {
            var value = led.Read();
            if (value == GpioPinValue.High)
                led.Write(GpioPinValue.Low);
            else
                led.Write(GpioPinValue.High);
        }

        //Thread.Sleep(10);
    }
}

}

No help for you but I have the same problem using a G400 Developer board.

I could not solve. Using TinyCLR 0.6.0
Same board running NETMF, button events work OK.

See here: Question on Button events - #6 by John_Brochue
It’s a known bug of version 0.6.0

I forgot all about the reply I received from GHI. Thanks for reminding me.

@Bauland - Hmmm, I looked at that post but I don’t think it’s quite the same problem. That post states that the event doesn’t fire but my problem is that it seems to fire twice. I did try what John suggested and “Read” the button but it’s value is “High” on both passes so no help there.

Thanks for the post though.

How did you determine that Read is high on both occurrences of the event?

My bad, the Read is low on first pass and high on the next. I was probably looking at the wrong variable when debugging. so this does work.

Thanks for the help!

using System;
using System.Collections;
using System.Text;
using System.Threading;
using GHIElectronics.TinyCLR.Devices.Gpio;
using GHIElectronics.TinyCLR.Pins;

namespace ButtonLedExperiment
{
class Program
{
static GpioPin led;
static void Main()
{
led = GpioController.GetDefault().OpenPin(FEZSpider.GpioPin.Socket4.Pin4);
led.SetDriveMode(GpioPinDriveMode.Output);
led.Write(GpioPinValue.Low);

        var button = GpioController.GetDefault().OpenPin(FEZSpider.GpioPin.Socket4.Pin3);
        button.SetDriveMode(GpioPinDriveMode.InputPullUp);
        button.ValueChanged += Button_ValueChanged;

        Thread.Sleep(-1);
    }

    private static void Button_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs e)
    {

        GpioPinValue value = sender.Read();

        if (value == GpioPinValue.Low)
        {
            if (led.Read() == GpioPinValue.High)
                led.Write(GpioPinValue.Low);
            else
                led.Write(GpioPinValue.High);
        }
    }
}

}

In case anyone is interested and a lot of you might know this but Gravicode has an effort to port Gadgeteer to TinyCLR https://github.com/Gravicode/GadgeteerToTinyCLR

I used the Button class but had to make some minor changes to it because of the Edge problem.

using GHIElectronics.TinyCLR.Devices.Gpio;
//using GT = Gadgeteer;
//using GTI = Gadgeteer.SocketInterfaces;
//using GTM = Gadgeteer.Modules;

//namespace Gadgeteer.Modules.GHIElectronics
namespace ButtonExp
{
///

A Button module for .NET Gadgeteer
public class Button
{
private GpioPin input;
private GpioPin led;
private LedMode currentMode;

    private ButtonEventHandler onButtonEvent;

    /// <summary>Represents the delegate that is used to handle the <see cref="ButtonReleased" /> and <see cref="ButtonPressed" /> events.</summary>
    /// <param name="sender">The <see cref="Button" /> object that raised the event.</param>
    /// <param name="state">The state of the Button</param>
    public delegate void ButtonEventHandler(Button sender, ButtonState state);

    /// <summary>Raised when the button is released.</summary>
    public event ButtonEventHandler ButtonReleased;

    /// <summary>Raised when the button is pressed.</summary>
    public event ButtonEventHandler ButtonPressed;

    /// <summary>Whether or not the button is pressed.</summary>
    public bool Pressed
    {
        get
        {
            return !(this.input.Read() == GpioPinValue.High);
        }
    }

    /// <summary>Whether or not the LED is currently on or off.</summary>
    public bool IsLedOn
    {
        get
        {
            return this.led.Read() == GpioPinValue.High;
        }
    }

    /// <summary>Gets or sets the LED's current mode of operation.</summary>
    public LedMode Mode
    {
        get
        {
            return this.currentMode;
        }

        set
        {
            this.currentMode = value;

            if (this.currentMode == LedMode.On || (this.currentMode == LedMode.OnWhilePressed && this.Pressed) || (this.currentMode == LedMode.OnWhileReleased && !this.Pressed))
                this.TurnLedOn();
            else if (this.currentMode == LedMode.Off || (this.currentMode == LedMode.OnWhileReleased && this.Pressed) || (this.currentMode == LedMode.OnWhilePressed && !this.Pressed))
                this.TurnLedOff();
        }
    }

    /// <summary>The state of the button.</summary>
    public enum ButtonState
    {

        /// <summary>The button is pressed.</summary>
        Pressed = 0,

        /// <summary>The button is released.</summary>
        Released = 1
    }

    /// <summary>The various modes a LED can be set to.</summary>
    public enum LedMode
    {

        /// <summary>The LED is on regardless of the button state.</summary>
        On,

        /// <summary>The LED is off regardless of the button state.</summary>
        Off,

        /// <summary>The LED changes state whenever the button is pressed.</summary>
        ToggleWhenPressed,

        /// <summary>The LED changes state whenever the button is released.</summary>
        ToggleWhenReleased,

        /// <summary>The LED is on while the button is pressed.</summary>
        OnWhilePressed,

        /// <summary>The LED is on except when the button is pressed.</summary>
        OnWhileReleased
    }

    /// <summary>Constructs a new instance.</summary>
    /// <param name="DigitalPin3">The mainboard pin that has digital pin.</param>
    /// <param name="DigitalPin4">The mainboard pin that has digital pin.</param>
    public Button(int DigitalPin3, int DigitalPin4)
    {
        //Socket socket = Socket.GetSocket(socketNumber, true, this, null);

        //socket.EnsureTypeIsSupported(new char[] { 'X', 'Y' }, this);

        this.currentMode = LedMode.Off;
        var controller = GpioController.GetDefault();
        this.led = controller.OpenPin(DigitalPin4);
        this.led.SetDriveMode(GpioPinDriveMode.Output);

        this.input = controller.OpenPin(DigitalPin3);//GTI.InterruptInputFactory.Create(socket, GT.Socket.Pin.Three, GTI.GlitchFilterMode.On, GTI.ResistorMode.PullUp, GTI.InterruptMode.RisingAndFallingEdge, this);
        if (input.IsDriveModeSupported(GpioPinDriveMode.InputPullUp))
            input.SetDriveMode(GpioPinDriveMode.InputPullUp);
        else
            input.SetDriveMode(GpioPinDriveMode.Input);

        //this.input.Interrupt += this.OnInterrupt;
        input.ValueChanged += Input_ValueChanged; ;
    }

    // Dan Miller 12/3/2017: Modified the event methode because pin edge is not reporting correctly with TinyCLR v0.6.0.
    //private void Input_ValueChanged(object sender, GpioPinValueChangedEventArgs e)
    private void Input_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs e)
    {
        //var state = e.Edge == GpioPinEdge.FallingEdge ? ButtonState.Released : ButtonState.Pressed;
        var state = sender.Read() == GpioPinValue.High ? ButtonState.Released : ButtonState.Pressed;

        switch (state)
        {
            case ButtonState.Released:
                if (this.Mode == LedMode.OnWhilePressed)
                    this.TurnLedOff();
                else if (this.Mode == LedMode.OnWhileReleased)
                    this.TurnLedOn();
                else if (this.Mode == LedMode.ToggleWhenReleased)
                    this.ToggleLED();

                break;

            case ButtonState.Pressed:
                if (this.Mode == LedMode.OnWhilePressed)
                    this.TurnLedOn();
                else if (this.Mode == LedMode.OnWhileReleased)
                    this.TurnLedOff();
                else if (this.Mode == LedMode.ToggleWhenPressed)
                    this.ToggleLED();

                break;
        }

        this.OnButtonEvent(this, state);
    }


    /// <summary>Turns on the LED.</summary>
    public void TurnLedOn()
    {
        this.led.Write(GpioPinValue.High);
    }

    /// <summary>Turns off the LED.</summary>
    public void TurnLedOff()
    {
        this.led.Write(GpioPinValue.Low);
    }

    /// <summary>Turns the LED off if it is on and on if it is off.</summary>
    public void ToggleLED()
    {
        if (this.IsLedOn)
            this.TurnLedOff();
        else
            this.TurnLedOn();
    }

    /*
     private void OnInterrupt(GTI.InterruptInput input, bool value)
    {
        var state = value ? ButtonState.Released : ButtonState.Pressed;

        switch (state)
        {
            case ButtonState.Released:
                if (this.Mode == LedMode.OnWhilePressed)
                    this.TurnLedOff();
                else if (this.Mode == LedMode.OnWhileReleased)
                    this.TurnLedOn();
                else if (this.Mode == LedMode.ToggleWhenReleased)
                    this.ToggleLED();

                break;

            case ButtonState.Pressed:
                if (this.Mode == LedMode.OnWhilePressed)
                    this.TurnLedOn();
                else if (this.Mode == LedMode.OnWhileReleased)
                    this.TurnLedOff();
                else if (this.Mode == LedMode.ToggleWhenPressed)
                    this.ToggleLED();

                break;
        }

        this.OnButtonEvent(this, state);
    }
    */
    private void OnButtonEvent(Button sender, ButtonState state)
    {
        if (this.onButtonEvent == null)
            this.onButtonEvent = this.OnButtonEvent;
        switch (state)
        {
            case ButtonState.Released:
                if (ButtonReleased != null)
                    this.ButtonReleased(sender, state); break;
            case ButtonState.Pressed:
                if (ButtonPressed != null)
                    this.ButtonPressed(sender, state); break;
        }
        /*
        if (Program.CheckAndInvoke(state == ButtonState.Released ? this.ButtonReleased : this.ButtonPressed, this.onButtonEvent, sender, state))
        {
            switch (state)
            {
                case ButtonState.Released: this.ButtonReleased(sender, state); break;
                case ButtonState.Pressed: this.ButtonPressed(sender, state); break;
            }
        }*/
    }
}

}