Nerves Livebook から RGB LED を制御する（ソフトPWMもやってみる）

## はじめに

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)

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

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

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

## RGB LED

4本足が出ている LED です

赤、緑、青の 3 つの LED が中に入っています

![IMG_1558.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/81381fda-3eb3-dd39-8c17-516cdbc1b729.jpeg)

## 回路の組み立て

公開されているオンライン説明書に従って回路を組み立てます

https://docs.sunfounder.com/projects/davinci-kit/ja/latest/1.1.2_rgb_led.html

RGB LED は足の長さでどの線なのか見極める必要があります

## ピンを開く

単純な LED のときと同様、まずは GPIO ピンを開いて使えるようにします

赤、緑、青にそれぞれ対応したピンを指定しています

```elixir
{:ok, red} = Circuits.GPIO.open("GPIO17", :output)
{:ok, green} = Circuits.GPIO.open("GPIO18", :output)
{:ok, blue} = Circuits.GPIO.open("GPIO27", :output)
```

## LED の点灯

どの色を光らせるかという組み合わせで色が決まります

```elixir
# Red
Circuits.GPIO.write(red, 1)
Circuits.GPIO.write(green, 0)
Circuits.GPIO.write(blue, 0)
```

![IMG_1560.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/c6bde6c5-ecb4-da4c-74cd-991e7a9561c0.jpeg)

```elixir
# Green
Circuits.GPIO.write(red, 0)
Circuits.GPIO.write(green, 1)
Circuits.GPIO.write(blue, 0)
```

![IMG_1561.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/1f8048ed-b24a-828d-d4f1-dbbdb2d3bb58.jpeg)

```elixir
# Blue
Circuits.GPIO.write(red, 0)
Circuits.GPIO.write(green, 0)
Circuits.GPIO.write(blue, 1)
```

![IMG_1562.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/da770d2a-f014-a7e0-983d-aa06a76f0745.jpeg)

```elixir
# Yellow
Circuits.GPIO.write(red, 1)
Circuits.GPIO.write(green, 1)
Circuits.GPIO.write(blue, 0)
```

![IMG_1563.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/93595487-dc19-c477-a0fd-1af1cae9d404.jpeg)

```elixir
# Purple
Circuits.GPIO.write(red, 1)
Circuits.GPIO.write(green, 0)
Circuits.GPIO.write(blue, 1)
```

![IMG_1564.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/acb19698-bb8b-e7e4-9427-8ad4c146123f.jpeg)

```elixir
# White
Circuits.GPIO.write(red, 1)
Circuits.GPIO.write(green, 1)
Circuits.GPIO.write(blue, 1)
```

![IMG_1565.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/b4c70703-af87-7514-c198-ab6a43ef406e.jpeg)

この辺りは子どもと一緒にやると「これとこれを一緒に光らせたら何色になるでしょうか」といった感じで盛り上がりました

## PWM

Pulse Width Modulation = パルス幅変調を利用して LED の明るさを調整します

@mnishiguchi さんの記事で分かりやすく解説しています

https://qiita.com/mnishiguchi/items/4bdf88acf0ab0e8e2c7e

今回の記事ではソフト PWM を実行してみました

:::note warn
Raspberry Pi 0 〜 3 の場合は Nerves Livebook のファームウェアが Pigpiox インストール済のものになっています

https://github.com/tokafish/pigpiox

Pigpiox があれば簡単に PWM を実行でき、サンプルコードも公開されています

https://github.com/nerves-livebook/nerves_livebook/blob/main/priv/samples/basics/pwm.livemd

残念ながら Raspberry Pi 4 では Pigpiox がインストールされていません

以下の箇所を参照

https://github.com/nerves-livebook/nerves_livebook/blob/main/mix.exs

```elixir
...
  # Libraries that use MMAL on the Raspberry Pi won't work with the Raspberry
  # Pi 4. The Raspberry Pi 4 uses DRM and libcamera.
  @rpi_mmal_targets [:rpi, :rpi0, :rpi2, :rpi3, :rpi3a]
...
      {:pigpiox, "~> 0.1", targets: @rpi_mmal_targets},
...
```
:::

まず、一旦すべての LED を消しておきます

```elixir
Circuits.GPIO.write(red, 0)
Circuits.GPIO.write(green, 0)
Circuits.GPIO.write(blue, 0)
```

PWM 用のモジュールを定義します

```elixir
defmodule PWM do
  def set(led, duty, length) do
    on_time = 10 * duty |> round()
    off_time = 10 - on_time
    full_time = length / 10 |> round()
    1..full_time
    |> Enum.map(fn _ ->
      Circuits.GPIO.write(led, 0)
      Process.sleep(off_time)
      Circuits.GPIO.write(led, 1)
      Process.sleep(on_time)
    end)
  end
end
```

10 msec のうち、第2引数（duty）に指定した割合の分だけ LED を ON にして、残りは OFF にします

例えば割合が 0.3 の場合、 3 msec OFF で 7 msec ON というのを繰り返します

これにより、ずっと ON の場合と比べて LED が暗くなります

次のように段階的に割合を上げることで明るさの変化を確認してみましょう

```elixir
PWM.set(red, 0.3, 1000)
PWM.set(red, 0.6, 1000)
PWM.set(red, 1.0, 1000)
```

![pwm.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/19fd4baa-618b-452c-230c-139568de0bf9.gif)

徐々に明るくなっています

これを利用して、緑 100% + 青 20% のシアンを作ってみます

まず、緑だけ光らせます

```elixir
Circuits.GPIO.write(red, 0)
Circuits.GPIO.write(green, 1)
Circuits.GPIO.write(blue, 0)
```

この状態で青を 20% 光らせます

```elixir
# Cyan
PWM.set(blue, 0.2, 5000)
```

![IMG_1567.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/9ca62bef-275f-3654-6e1f-cb764cb37587.jpeg)

上手くシアンが表現できました

## ピンを閉じる

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

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

## まとめ

RGB LED を様々な色に光らせることができました

PWM は少し難しいところもありましたが、とりあえず実装できました