本文へスキップ
ysk-hack-log
戻る

BlazorのWeb画面からGPIOを操作する|LEDとボタン(Raspberry Pi)【連載③】

「BlazorのWeb画面からGPIOを操作する|LEDとボタン(Raspberry Pi)【連載③】」の記事アイキャッチ

前回(第2回) でBlazorアプリがPi上で動きました。今回はいよいよハードに触れます。LEDと押しボタンをGPIOにつなぎ、Webの画面からLEDを点灯/消灯し、ボタンの状態を表示します。

Table of contents

Open Table of contents

用意するもの

GPIOの基本操作には raspi-config での有効化は不要です(SPI/I2Cは第4回で有効化します)。

配線

ピン番号は BCM(GPIO番号) で指定します(System.Device.Gpio の既定は論理=BCM番号)。

配線図

System.Device.Gpio を追加

プロジェクト(第2回の TempMonitor)で、.NET IoT の GPIO パッケージを追加します。

dotnet add package System.Device.Gpio

GPIOをサービスにする(DI)

GPIOはアプリ全体で共有するハードウェア資源なので、GpioController をシングルトンのサービスにまとめます。

// Services/GpioService.cs
using System.Device.Gpio;

public sealed class GpioService : IDisposable
{
    private const int LedPin = 18;
    private const int ButtonPin = 24;
    private readonly GpioController _controller = new();
    private readonly object _gate = new(); // 複数スレッドからの同時アクセスを直列化

    public GpioService()
    {
        _controller.OpenPin(LedPin, PinMode.Output);
        _controller.OpenPin(ButtonPin, PinMode.InputPullUp);
    }

    public void SetLed(bool on)
    {
        lock (_gate)
            _controller.Write(LedPin, on ? PinValue.High : PinValue.Low);
    }

    // 内部プルアップのため、押している間だけ Low になる
    public bool IsButtonPressed()
    {
        lock (_gate)
            return _controller.Read(ButtonPin) == PinValue.Low;
    }

    public void Dispose() => _controller.Dispose();
}

Program.cs に登録します。

builder.Services.AddSingleton<GpioService>();

Blazorの画面から操作する

対話操作が必要なので、コンポーネントに @rendermode InteractiveServer を付けます(第1回で選んだ Blazor Server の対話モード)。

@* Components/Pages/Gpio.razor *@
@page "/gpio"
@rendermode InteractiveServer
@implements IDisposable
@inject GpioService GpioSvc

<h1>GPIO操作</h1>

<button class="btn btn-primary" @onclick="ToggleLed">
    LEDを@(ledOn ? "消す" : "点ける")
</button>

<p>ボタンの状態:<b>@(pressed ? "押されています" : "離れています")</b></p>

@code {
    private bool ledOn;
    private bool pressed;
    private Timer? _timer;

    protected override void OnInitialized()
    {
        // 200msごとにボタン状態を読み、変化があれば再描画
        _timer = new Timer(_ =>
        {
            var now = GpioSvc.IsButtonPressed();
            if (now != pressed)
            {
                pressed = now;
                InvokeAsync(StateHasChanged);
            }
        }, null, 0, 200);
    }

    private void ToggleLed()
    {
        ledOn = !ledOn;
        GpioSvc.SetLed(ledOn);
    }

    public void Dispose() => _timer?.Dispose();
}

ここではボタン状態を簡易的にポーリングしています。よりスマートなリアルタイム更新は第5回(SignalR)で扱います。

実行

必ずRaspberry Pi上で実行します(開発PCにはGPIOが無いため例外になります)。

dotnet run --urls "http://0.0.0.0:5000"

別PCのブラウザで http://<PiのIP>:5000/gpio を開き、ボタンを押すとLEDが点灯/消灯、実機のボタンを押すと画面表示が変われば成功です。

ブラウザ画面

つまずきポイント

まとめ

次回(第4回)は、いよいよ温度センサー。アナログの LM35DZ を、MCP3208(SPIのADC)経由で読む方法を扱います。


前の記事:連載② Raspberry Piに.NETを入れて最初のBlazorアプリを動かす


この記事をシェア:

前の記事
Raspberry Piに.NETを入れて最初のBlazorアプリを動かす【連載②】
次の記事
MCP3208+LM35DZでアナログ温度を読む|Raspberry PiのSPIとADC【連載④】