Nerves Livebook でフォトレジスター（光センサー）からアナログ値を取得してデジタル値に変換する

## はじめに

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)
- [Nerves Livebook から LED 棒グラフを制御する](https://qiita.com/RyoWakabayashi/items/a6e8b712388d68bdca44)
- [Nerves Livebook でボタンとLEDを連動させる](https://qiita.com/RyoWakabayashi/items/9bf9475d3c5d65cdfa4e)
- [Nerves Livebook で 7 セグメントディスプレイに文字を表示する](https://qiita.com/RyoWakabayashi/items/d13d86d91c86e816d899)
- [Nerves Livebook で 4 桁 7 セグメントディスプレイに文字を表示する](https://qiita.com/RyoWakabayashi/items/32edd890ade613cb4e37)
- [Nerves Livebook で LED ドットマトリックスにアニメーションを表示する](https://qiita.com/RyoWakabayashi/private/16f110cdd4febd0aedc1)
- [Nerves Livebook でアクティブブザーを鳴らす](https://qiita.com/RyoWakabayashi/private/b932c04c3050c22b373f)
- [Nerves Livebook でパッシブブザーを演奏しドレミの歌を奏でる](https://qiita.com/RyoWakabayashi/items/c203e40bd2004cb95aae)
- [Nerves Livebook でポテンショメーターからアナログ値を取得してデジタル値に変換する](https://qiita.com/RyoWakabayashi/private/ea1a620c3114b064dc2c)

この記事ではフォトレジスターで明るさを検知し、LEDの光の強弱を制御します

また、必要なモジュールを追加するため、 Nerves Livebook をカスタマイズします

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

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

## フォトレジスター

当たっている光の強さによって抵抗値が変化します

![IMG_1644.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/55613528-e38d-30cf-477b-b0df6e222853.jpeg)

## ADC0834

ADC0834 は AD 変換器（AD コンバーター）です

![IMG_1643.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/71e7b9a2-5402-35a6-3b3f-5676e5ed169d.jpeg)

REF の電圧（5V）を基準として、CH0〜3の入力電圧（アナログ値）がどの程度なのか 0 〜　255 の 256 段階（8 bit）のデジタル値に変換します

## 回路の組み立て

オンライン説明書の通りに回路を組みます

ただし、 **LED に接続するピンはPWM対応が必須であるため、 GPIO22 ではなく GPIO12 を使います**

https://docs.sunfounder.com/projects/davinci-kit/ja/latest/2.2.1_photoresistor.html

## Nerves Livebook のカスタマイズ

以下の記事に従って Pigpiox 追加済の Nerves Livebook を使用します

https://qiita.com/RyoWakabayashi/private/9df8d216d6ffeca8c6d5

## ノートブックの実装

Nerves Livebook を開き、以降のコードを実行していきます

コードの内容は前回の記事と全く同じです

https://qiita.com/RyoWakabayashi/private/ea1a620c3114b064dc2c

### ピンを開く

ADC0834 を制御するためのピンを開きます

```elixir
{:ok, cs} = Circuits.GPIO.open("GPIO17", :output)
{:ok, clk} = Circuits.GPIO.open("GPIO18", :output)
{:ok, dio} = Circuits.GPIO.open("GPIO27", :output)
```

`dio` は DI と DO の両方に接続し、必要に応じて入出力を切り替えて使用します

### モジュール定義

ADC0834 を制御するモジュールを定義します

```elixir
defmodule ADC do
  import Bitwise

  def get_result(dio, clk, cs, channel \\ 0) do
    sel = if channel > 1, do: 1, else: 0
    odd = channel &&& 1

    # AD 変換開始
    Circuits.GPIO.write(cs, 0)
    start_adc(dio, clk)

    # シングルエンド信号を使用（1本の信号だけを読む）
    single_end_mode(dio, clk)

    # チャンネル設定（デフォルトは CH0 を使用）
    set_odd(dio, clk, odd)
    set_sel(dio, clk, sel)

    # dio を入力に設定
    Circuits.GPIO.set_direction(dio, :input)

    # AD変換を実行し、各桁の値を取得
    dat1 =
      0..7
      |> Enum.reduce(0, fn _i, acc ->
        Circuits.GPIO.write(clk, 1)
        :timer.sleep(2)
        Circuits.GPIO.write(clk, 0)
        :timer.sleep(2)

        Circuits.GPIO.set_direction(dio, :input)

        bit = Circuits.GPIO.read(dio)
        acc = acc <<< 1 ||| bit
        acc
      end)

    # AD変換完了後に改めて逆順で各桁の値を取得
    dat2 =
      0..7
      |> Enum.reduce(0, fn i, acc ->
        bit = Circuits.GPIO.read(dio)
        acc = acc ||| bit <<< i
        Circuits.GPIO.write(clk, 1)
        :timer.sleep(2)
        Circuits.GPIO.write(clk, 0)
        :timer.sleep(2)

        acc
      end)

    # AD 変換終了
    Circuits.GPIO.write(cs, 1)
    Circuits.GPIO.set_direction(dio, :output)

    # 変換中と変換後の値が等しければ出力する
    if dat1 == dat2, do: dat1, else: 0
  end

  defp start_adc(dio, clk) do
    Circuits.GPIO.write(clk, 0)
    Circuits.GPIO.write(dio, 1)
    :timer.sleep(2)
    Circuits.GPIO.write(clk, 1)
    :timer.sleep(2)
  end

  defp single_end_mode(dio, clk) do
    Circuits.GPIO.write(clk, 0)
    Circuits.GPIO.write(dio, 1)
    :timer.sleep(2)
    Circuits.GPIO.write(clk, 1)
    :timer.sleep(2)
  end

  defp set_odd(dio, clk, odd) do
    Circuits.GPIO.write(clk, 0)
    Circuits.GPIO.write(dio, odd)
    :timer.sleep(2)
    Circuits.GPIO.write(clk, 1)
    :timer.sleep(2)
  end

  defp set_sel(dio, clk, sel) do
    Circuits.GPIO.write(clk, 0)
    Circuits.GPIO.write(dio, sel)
    :timer.sleep(2)
    Circuits.GPIO.write(clk, 1)
    :timer.sleep(2)
    Circuits.GPIO.write(clk, 0)
    :timer.sleep(2)
  end
end
```

### AD変換した結果を取得する

`ADC.get_result` でフォトレジスターのアナログ値を ADC0834 がデジタル値に変換した結果の値を取得します

```elixir
analog_val = ADC.get_result(dio, clk, cs)
```

### LEDの点灯

0 〜 255 の値をデューティー比として PWM を実行し、 LED の光の強弱を制御します

```elixir
duty = round(analog_val * 1_000_000 / 255)
duty / 10000
```

```elixir
Pigpiox.Pwm.hardware_pwm(12, 2000, duty)
```

自然光の場合（7月の正午、窓からの光のみ）

検出値は 38 = 約 15 %

LED はほんのり光ります

![IMG_1645.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/d235a95b-e5e4-3c8d-f138-d5ba9fbc304a.jpeg)

ライトを目の前に当てた状態

検出値は 2 でほぼ 0 %

LED はほぼ光りません

![IMG_1647.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/8889f039-ab8b-d27f-07c4-0a1466a347b1.jpeg)

フォトレジスターを蓋（手持ち扇風機を立てる台）で覆った状態

検出値は 161 でおよそ 63 %

LED は強めに光ります

![IMG_1649.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/a210c846-a74d-37e1-b440-83b15c48c82a.jpeg)

### フォトレジスターとLEDを連動させる

一定期間ポテンショメーターの値を取得してLEDを光らせる処理を繰り返し、連動させます

```elixir
1..300
|> Enum.map(fn _i ->
  analog_val = ADC.get_result(dio, clk, cs)
  duty = round(analog_val * 1_000_000 / 255)
  IO.inspect(duty / 10000)
  Pigpiox.Pwm.hardware_pwm(12, 2000, duty)
end)
```

フォトレジスターにライトを近づけると LED が暗くなり、フォトレジスターを指でつまむと LED が明るくなります

<iframe width="455" height="808" src="https://www.youtube.com/embed/nVkEdllPM-8" title="フォトレジスター（光センサー）とLEDを連動させる" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

### ピンを閉じる

遊び終わったらピンを閉じます

```elixir
Circuits.GPIO.close(cs)
Circuits.GPIO.close(clk)
Circuits.GPIO.close(dio)
```

## まとめ

フォトレジスターと LED を連動させて、暗いときは強く光り、明るいときは弱く光るような制御が実装できました

色んなセンサーで試したいですね