最初のボタン

最初のLチカができたので、次は物理的なボタンをつけてみましょう。
Lチカの場合は、電流の出力系で、物理ボタンの場合は、電流の入力系なのでワンセットで試しておきます。

# ボタンのサンプル

[Push button | Windows IoT](https://developer.microsoft.com/en-us/windows/iot/samples/pushbutton "Push button | Windows IoT")

![](https://az835927.vo.msecnd.net/sites/iot/Resources/images/PushButton/PushButtonSample.png)

物理ボタンを押したら、LED が光るサンプルがあります。ここでは、えらいきちんとしたボタンを使っていますが、30円のタクトスイッチとか、そもそもボタンを使わずにジャンパワイヤー同士を接触させるだけでも構いません。

ちょっと高めですが、千石電子  [せんごくネット通販](https://www.sengoku.co.jp/mod/sgk_cart/search.php?cid=3541 "せんごくネット通販") に行くとアーケードゲーム用のボタンを売っているので、これを使うこともできます。

配線はこんな感じで、

- 5番ピンにボタン
- 6番ピンにLED

をつけます。例によって、ジャンパーワイヤーの色は自由に決めて構いません。

![001.jpg](https://qiita-image-store.s3.amazonaws.com/0/32488/8138e04a-2683-1923-953b-b492614ad574.jpeg)

# コードを見る

参照設定で「Windows IoT Extension SDK」を追加しておくところは同じで、先頭の行に「Windows.Devices.Gpio」を追加。

```csharp
using Windows.Devices.Gpio;
```

初期化は起動時に InitGPIO メソッド内で行っています。

```csharp
private void InitGPIO()
{
    var gpio = GpioController.GetDefault();

    // Show an error if there is no GPIO controller
    if (gpio == null)
    {
        GpioStatus.Text = "There is no GPIO controller on this device.";
        return;
    }

    buttonPin = gpio.OpenPin(BUTTON_PIN);
    ledPin = gpio.OpenPin(LED_PIN);

    // Initialize LED to the OFF state by first writing a HIGH value
    // We write HIGH because the LED is wired in a active LOW configuration
    ledPin.Write(GpioPinValue.High); 
    ledPin.SetDriveMode(GpioPinDriveMode.Output);

    // Check if input pull-up resistors are supported
    if (buttonPin.IsDriveModeSupported(GpioPinDriveMode.InputPullUp))
        buttonPin.SetDriveMode(GpioPinDriveMode.InputPullUp);
    else
        buttonPin.SetDriveMode(GpioPinDriveMode.Input);

    // Set a debounce timeout to filter out switch bounce noise from a button press
    buttonPin.DebounceTimeout = TimeSpan.FromMilliseconds(50);

    // Register for the ValueChanged event so our buttonPin_ValueChanged 
    // function is called when the button is pressed
    buttonPin.ValueChanged += buttonPin_ValueChanged;

    GpioStatus.Text = "GPIO pins initialized correctly.";
}
```

1. GpioController.GetDefault で gpio を取得
2. gpio.OpenPin で BUTTON_PIN(5) をオープン
3. gpio.OpenPin で LED_PIN(6) をオープン
4. ledPin.SetDriveMode で、LED をアウトプットモード（出力モード）に設定
5. buttonPin.SetDriveMode でボタンをインプットモード（入力モード）に設定。ピンによってプルアップなどの設定があるので、これを IsDriveModeSupported 自動判別しています。
6. buttonPin.DebounceTimeout でチャタリングの時間間隔を設定。物理ボタンの場合、ボタンを押した瞬間は ON/OFF が交互に現れるチャタリングという現象が起こります。このために、50msec ON/OFF の状態が続いたらボタンが変化したという設定になります。これは、スマートフォンやディスプレイでは起こらない現象（実は内部的に起こっているけど、ソフトウェア的に回避している）ので、物理ボタン特有の面白いところです。
7. buttonPin.ValueChanged で、ボタンの変更時にイベントを発生させます。ここは、Windows アプリケーションを作ったときのイベント処理と同じです。

次に、ボタンの状態が変更したときのイベントを作ります。

```csharp
private void buttonPin_ValueChanged(GpioPin sender, GpioPinValueChangedEventArgs e)
{
    // toggle the state of the LED every time the button is pressed
    if (e.Edge == GpioPinEdge.FallingEdge)
    {
        ledPinValue = (ledPinValue == GpioPinValue.Low) ?
            GpioPinValue.High : GpioPinValue.Low;
        ledPin.Write(ledPinValue);
    }

    // need to invoke UI updates on the UI thread because this event
    // handler gets invoked on a separate thread.
    var task = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
        if (e.Edge == GpioPinEdge.FallingEdge)
        {
            ledEllipse.Fill = (ledPinValue == GpioPinValue.Low) ? 
                redBrush : grayBrush;
            GpioStatus.Text = "Button Pressed";
        }
        else
        {
            GpioStatus.Text = "Button Released";
        }
    });
}
```

1. ボタンの状態は、e.Edge でチェックをします。GpioPinEdge.FallingEdge は、状態が変化したという印です。
2. Dispatcher.RunAsync でスレッドを切り替えているのは、物理ボタンの変更イベント（ValueChangedイベント）は UI スレッドではないからです。通常の　Windows アプリのボタンは Click イベントで直接テキストボックスのTextプロパティの値を変更できますが、Windows IoT Core の場合は画面に出力するときは、Dispatcher.RunAsync でスレッドを切り替えます。

これを Raspberry Pi 上で動かすと、ボタンを押したときに LED が光るようになります。もうちょっと工夫すれば、Amazon Dush Button のように、ボタンを押したらインターネット経由で Amazon に注文ができるようにできそうですよね。