Blazor serverをラズパイに入れてFAしたい

この記事はラズパイをBlazor serverで動かす記事です。
FA = factory automationです。

この記事では、「Blazor」はBlazor serverを指しています。
webの話ではなくて恐縮なのですが、ちょっと毛色の違う話として気分転換がてら眺めて頂けると嬉しいです。


※変更履歴

- 2021/12/27
    - 「3.ラズパイを遠隔操作」の章に、ラズパイへの複数アクセス問題の解決方法についての記述を追加

# なぜラズパイにBlazor？
ラズパイはそのお手軽さから、近年ではハード開発現場や生産ラインなどで多く活用されています。
私も例にもれず、製品評価ジグや自動測定などで利用しています。

自分用の評価向けだったら自由に作れば良いのですが、例えば生産ライン向けとなると、非エンジニアの人も使えるようにしないといけません。
そうなると、操作のためのGUIが欲しいところです。

ラズパイのOSはLinuxなのでWinFormsやWPFが使えず、GUIを作ろうとするとQtやGTK（mono）などで作成することになるのですが、これが結構大変でした。
苦労していたところにBlazorと出会いまして、HTMLで画面をつくれるというので導入してみたところ、意外とすんなり作れたので、これを機会に紹介してみます。

# 環境

- RaspberryPi 3B  

- dotnet Version:   5.0.404


# 準備
Blazorをラズパイ向けにビルドするには、下記のページを参照ください。
生成された「publish」フォルダをラズパイにコピーして実行します。

https://docs.microsoft.com/ja-jp/dotnet/iot/deployment#deploying-a-self-contained-app


ラズパイとwindows間のファイルのやり取りは、Sambaが便利です。
Windowsからラズパイへエクスプローラでアクセスできるので、とてもお手軽です。
インストール手順をまとめてますのでよかったら参照ください。

https://yamaccu.github.io/tils/20211018-Raspberry-Samba

ラズパイについての説明は省略します。


# 1. BlazorとラズパイでＬチカする
Blazorでラズパイ上に出ているピンの電圧レベルを操作することができます。
ピンにLEDをつないで光らせたり、接点ユニットをつないで接点出力したりなどに利用できます。
今回は、シンプルにLチカしてみようと思います。

microsoft公式に.netでラズパイを制御する方法がありますので、こちらに沿って作ります。

https://docs.microsoft.com/ja-jp/dotnet/iot/tutorials/blink-led

## LED ON/OFFボタンの追加

こんな感じで、BlazorのデフォルトページにLEDのON/OFFボタンを追加してみます。

![Lチカ2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519575/bda22d4f-ada5-406f-7af2-b6aa5f9d6132.png)


<details><summary>ソースコード</summary><div>

```C#
<button @onclick="ledOn">ON</button>
<button @onclick="ledOff">OFF</button>

@code{
    int pin = 18;
    private void ledOn()
    {
        using var controller = new GpioController();
        controller.OpenPin(pin, PinMode.Output);
        controller.Write(pin, PinValue.High);
    }
    private void ledOff()
    {
        using var controller = new GpioController();
        controller.OpenPin(pin, PinMode.Output);
        controller.Write(pin, PinValue.Low);
    }
}
```

</div></details>

ラズパイのGPIO18とGND間に、抵抗とLEDをつなぎます。
少し汚いですが、抵抗とLEDをピンヘッダにはんだ付けしました。
これをGPIO18とGNDのピンに差し込みます。
![led.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519575/d57d0463-36e5-8623-2db9-82b4976d6d29.png)

そうすると、ONを押すとLEDが光って、OFFを押すと消えるようになります。
Lチカの完成です。
![Lチカ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519575/89b74842-953e-2ccf-f01a-2a765c1c1b28.png)


# 2. Blazorとラズパイでシリアル通信する

組み込み系のプロダクトですと、大体シリアル通信ポートがついていて、シリアル通信でコマンドを送って評価・デバッグを行います。
ラズパイで自動で製品にコマンドを送信したり、製品から情報をとってきて画面表示したりできると評価がとても捗ります。

なので、シリアル通信を行ってみます。

## 通信用のボタン追加

先ほどのLチカの下に、こんな感じで操作ボタンを追加しました。

![serial.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519575/13971d12-4922-3258-5cb5-5d623ead2b29.png)

<details><summary>ソースコード</summary><div>

SerialPortクラスの使用には、system.io.portsをnugetから取得する必要があります。

COM Portセレクトボックス

```C#
<select @bind="@selectComport">
@foreach (var c in comport)
{
    <option value=@c>@c</option>
}
</select>
```

Baudrateセレクトボックス

```C#
<select @bind="@selectBaudrate">
@foreach (var b in baudrate)
{
    <option value=@b>@b</option>
}
</select>
```

接続 / 切断ボタン

```C#
<button @onclick="@connect">接続</button>
<button @onclick="@disconnect">切断</button>
```

送信データボックス / 送信ボタン

```C#
<input type="text" @bind="@sendData">
<button @onclick="@Send">Send</button>
```

受信データボックス

```C#
<input type="text" @bind="@recieveData">
```

C#コード部分

```C#
@code{
    SerialPort serialPort = new SerialPort();
    string[] comport = SerialPort.GetPortNames();
    string selectComport { get; set; } = "";
    int[] baudrate = { 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200 };
    int selectBaudrate { get; set; } = 0;
    string sendData { get; set; } = "";
    string recieveData { get; set; } = "";

    private void connect()
    {
        try
        {
            serialPort.PortName = selectComport;
            serialPort.BaudRate = selectBaudrate;
            serialPort.DataBits = 8;
            serialPort.Parity = Parity.None;
            serialPort.StopBits = StopBits.One;
            serialPort.WriteTimeout = 1000;
            serialPort.ReadTimeout = 1000;
            serialPort.Encoding = Encoding.UTF8;

            serialPort.Open();
            serialPort.DataReceived += OnReceived;
        }
        catch (Exception ex)
        {
            //エラー処理
        }
    }

    private void disconnect()
    {
        try
        {
            serialPort.Close();
        }
        catch (Exception ex)
        {
            //エラー処理
        }
    }

    private void Send()
    {
        if (serialPort.IsOpen)
        {
            serialPort.Write(sendData);
        }
    }

    private void OnReceived(object sender, SerialDataReceivedEventArgs e)
    {
        recieveData += serialPort.ReadExisting();
        InvokeAsync(() => StateHasChanged());
    }
}
```

</div></details>

COM PORTとBaudrateを選択して、接続ボタンを押すとシリアルポートがオープンします。
送信データボックスに何か文字を入力して、Sendを押すと、データが送信されます。
今回は自分で出した信号を自分でうけているので、受信ボックスに送ったデータが表示されます。

動作確認した系はこんな感じです。
![serial5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519575/e4fb4766-bf1c-209a-a29a-b40bc3d5453b.png)

画面はこんな感じです。
静止画だとよくわからないかもしれませんが、送信データを記入して送ると、それを受信して受信データ欄に表示されています。
![serial6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519575/4b9d5d48-c7e0-ac37-c39a-24d9f8c37924.png)

これでシリアル通信ができました。
特定コマンドを順番に送ったり、通信相手からの返答を認識したり、プログラムすれば自由自在です。
評価や生産が捗ってテンションがあがりますね。

ちなみに、USBシリアル変換基板は秋月電子で購入できます。

https://akizukidenshi.com/catalog/g/gM-11007/

# 3. ラズパイを遠隔操作
注）ラズパイを外部ネットワークに接続する場合はセキュリティ対策を行ってください。

ラズパイをネットワークに接続すれば、Windows PCのChromeなどのブラウザからラズパイ上のBlazorアプリにアクセスして、ラズパイを操作可能です。
実験室が離れている場合や、ホストPCから一括管理したい場合などにとても便利です。

Blazorのデフォルトでは外部アクセスできるようになっていません。
program.csを書き換える必要があります。

<details><summary>ソースコード</summary><div>

```C#
public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                //webBuilder.UseStartup<Startup>();    //★これは消す
                webBuilder.UseUrls("http://*:5000/").UseStartup<Startup>();    //★これを追加する
            });
    }
```

</div></details>


アクセスする手順は簡単で、<ラズパイのIPアドレス>:5000へアクセスするだけで画面が表示されます。
以下は、わかりづらいかもしれませんがwindows PCのChromeでラズパイで動いているBlazorアプリを表示しています。
![serial7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/519575/647c50a3-56ba-0c93-ead6-181614a33d6a.png)


ただし、この方法だと、ラズパイへ複数のPCからアクセスすることはできません。
一つのアクセスでシリアルポートが占有されてしまうためです。
複数PCからアクセスするためには、Server sideとWebAssenbly sideで分けて、WebAssenblyからの操作をServerに伝えるようにするなどすればできるかと思います。
（試せてないのですが。。）

※2021/12/27追記
上記複数アクセス問題の解決方法の一つとして、
DIコンテナにシリアル通信処理のクラスをシングルトンとして登録し、Blazorページへ@injectすれば、複数PCからラズパイへアクセスしても正常動作します。
詳細はコメント欄参照ください。
（@jsakamotoさんありがとうございました。）


# まとめ
ラズパイにBlazor Serverを入れてwebページで操作する方法を書いてみました。
WinFormsやWPFでwindowsアプリを作っていた人にとっては、HTMLだけ覚えれば結構とっつきやすいかなと思います。

用途としては、製品の自動テストや遠隔操作、生産ラインの自動化などに使えると思います。（使ってます）

ソースコードはgithubに上げています。

https://github.com/yamaccu/Blazor-RaspberryPi


以上です。みなさまのエンジニアライフの一助となれば幸いです。