Nerves Livebook から LED 棒グラフを制御する

## はじめに

Nerves Livebook で Raspberry Pi 用スターター電子工作キットを楽しんでいます

[SunFounder Raspberry Pi 用のスターター電子工作キット](https://amzn.asia/d/04vHhGKQ)

これまでの記事

- [Nerves Livebook で Raspberry Pi 4 のLチカ](https://qiita.com/RyoWakabayashi/items/8119260ceeea462d6b7b)
- [Nerves Livebook + Delux で簡単Lチカ](https://qiita.com/RyoWakabayashi/items/281afab89249dbf68cd5)
- [MacBook 上の Livebook から Raspberry Pi 上の Livebook に接続してLチカする](https://qiita.com/RyoWakabayashi/private/844da38ff24efa3b923c)
- [Nerves Livebook から GPIO 拡張ボードの LED を点滅させる](https://qiita.com/RyoWakabayashi/private/bda56ee6987b30857266)
- [Nerves Livebook から RGB LED を制御する（ソフトPWMもやってみる）](https://qiita.com/RyoWakabayashi/private/143fda67a1798bd788b7)

この記事では、工作キットに付いている LED 棒グラフを制御します

実装したノートブックはこちら

https://github.com/RyoWakabayashi/elixir-learning/blob/main/livebooks/nerves_livebook/gpio_led_bar.livemd

## LED 棒グラフ

白いところが LED になっていて、それぞれ光らせることができます

![IMG_1551.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/05a10623-46f5-b74d-a915-c45a74112dd2.jpeg)

## 回路の組み立て

工作キットのオンライン説明書に従って回路を組みます

https://docs.sunfounder.com/projects/davinci-kit/ja/latest/1.1.3_led_bar_graph.html

かなり線が多くなってきましたが、家族でわちゃわちゃしながら組み立てました

子どもでも簡単に組めるのが本当に良いです

抵抗を多数横並びにするため、足が長いのが邪魔になってきました

対処として正しいのか分かりませんが、ニッパーで短く切りました

![IMG_1552.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/fdbf944e-e537-e676-e7e8-43574ef2e9e2.jpeg)

Nerves Livebook の場合 SPICE0 のピンがすでに使われています（詳細までは追えていません）

そのため、 GPIO5 のピンを代わりに使いました

## ピンのステータス確認

以下のコードで GPIO のラベル一覧が取得できます

```elixir
Circuits.GPIO.enumerate()
|> Kino.DataTable.new()
```

![スクリーンショット 2024-06-23 21.18.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/e2eba408-7199-8dbd-362f-585cc18e47ca.png)

この情報を元に SPICE0 = label: SPI_CE0_N のピンを開いてみます

```elixir
Circuits.GPIO.open("SPI_CE0_N", :output)
```

すると、実行結果には `{:error, :already_open}` が返ってきます

当然、この状態では書き込むことができません

以下のコードでピンの状態が確認できます

```elixir
Circuits.GPIO.status("SPI_CE0_N")
```

すでに使われている場合、実行結果は以下のようになります

```elixir
{:ok, %{pull_mode: :none, direction: :output, consumer: "spi0 CS0"}}
```

他のピンの状態を確認し、使われていないものを代用しましょう

使われていない場合は以下のように `consumer: ""` となります

```elixir
{:ok, %{pull_mode: :none, direction: :input, consumer: ""}}
```

## LED の点灯

以下のコードで各ピンを開きます

```elixir
led_list =
  [
    "GPIO17",
    "GPIO18",
    "GPIO27",
    "GPIO22",
    "GPIO23",
    "GPIO24",
    "GPIO25",
    "SDA1",
    "SCL1",
    "GPIO5"
  ]
  |> Enum.map(fn label ->
    label
    |> Circuits.GPIO.open(:output)
    |> elem(1)
  end)
```

実行結果は以下のような配列になります

```elixir
[
  %Circuits.GPIO.CDev{ref: #Reference<0.2085008915.2670067723.197044>},
  %Circuits.GPIO.CDev{ref: #Reference<0.2085008915.2670067723.197045>},
  %Circuits.GPIO.CDev{ref: #Reference<0.2085008915.2670067723.197046>},
  %Circuits.GPIO.CDev{ref: #Reference<0.2085008915.2670067723.197047>},
  %Circuits.GPIO.CDev{ref: #Reference<0.2085008915.2670067723.197048>},
  %Circuits.GPIO.CDev{ref: #Reference<0.2085008915.2670067723.197049>},
  %Circuits.GPIO.CDev{ref: #Reference<0.2085008915.2670067723.197050>},
  %Circuits.GPIO.CDev{ref: #Reference<0.2085008915.2670067723.197051>},
  %Circuits.GPIO.CDev{ref: #Reference<0.2085008915.2670067723.197052>},
  %Circuits.GPIO.CDev{ref: #Reference<0.2085008915.2670067723.197053>}
]
```

オンライン説明書の回路の場合、各 LED は 0 でオン、 1 でオフになります

そのため、この時点で LED は全て点灯します

![IMG_1553.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/f56b75b8-de7e-c875-a803-3046777bee11.jpeg)

しかし、やはり直観に反しているのが気持ち悪いので 3V3 の線を GND に差し替え、 LED を反転させましょう

電源を入れ直して最初からやり直すと、以下のように全て消灯した状態になります

![IMG_1554.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/18f6b998-9a9e-9925-d1c9-bb924f099cfd.jpeg)

以下のコードを実行すると、上から4番目までが光ります

```elixir
Circuits.GPIO.write(led_list |> Enum.at(0), 1)
Circuits.GPIO.write(led_list |> Enum.at(1), 1)
Circuits.GPIO.write(led_list |> Enum.at(2), 1)
Circuits.GPIO.write(led_list |> Enum.at(3), 1)
Circuits.GPIO.write(led_list |> Enum.at(4), 0)
Circuits.GPIO.write(led_list |> Enum.at(5), 0)
Circuits.GPIO.write(led_list |> Enum.at(6), 0)
Circuits.GPIO.write(led_list |> Enum.at(7), 0)
Circuits.GPIO.write(led_list |> Enum.at(8), 0)
Circuits.GPIO.write(led_list |> Enum.at(9), 0)
```

![IMG_1555.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/f8ede31b-17f8-0291-5279-4433652b0031.jpeg)

下から指定番目までを光らせるモジュールを作ってみましょう

```elixir
defmodule LEDBar do
  def set(led_list, value) do
    led_list
    |> Enum.with_index()
    |> Enum.map(fn {led, index} ->
      Circuits.GPIO.write(led, (if (10-index) <= value, do: 1, else: 0))
    end)
  end
end
```

モジュールを使って LED 棒グラフを光らせます

```elixir
LEDBar.set(led_list, 6)
```

![IMG_1556.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/7ff8075b-4d47-a726-8855-9a4e36672a11.jpeg)

## LED 棒グラフの上昇

だんだん棒グラフが伸びるようにしてみましょう

```elixir
defmodule LEDElevator do
  def set(led_list, interval) do
    led_list
    |> Enum.map(fn led ->
      Circuits.GPIO.write(led, 0)
    end)

    Process.sleep(interval)

    led_list
    |> Enum.reverse()
    |> Enum.map(fn led ->
      Circuits.GPIO.write(led, 1)
      Process.sleep(interval)
    end)
  end
end
```

500 msec 間隔で上昇させてみます

```elixir
LEDElevator.set(led_list, 500)
```

![elevation.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/8ad8795e-0aaf-a127-9ce3-abc9872403a3.gif)

うまくいきました

## ピンを閉じる

最後にピンを閉じておきます

```elixir
led_list
|> Enum.map(fn led ->
  Circuits.GPIO.close(led)
end)
```

## まとめ

LED の数が増えても同じように制御できました

LED 棒グラフは割合を示すときに使えそうですね