すまあとリレー を Nerves で作ってみた

これは [#NervesJP Advent Calendar 2021](https://qiita.com/advent-calendar/2021/elixir) の4日目です。昨日は [@torifukukaiou](https://qiita.com/torifukukaiou) さんの [elixir-desktop/desktop のandroidサンプルをイゴかす](https://qiita.com/torifukukaiou/items/5458458e2ec1bcee5152) でした。

# はじめに
ソフトウェアエンジニアが IoT や FA をするときに、ハードウェアの知識やノウハウを避けて通るわけには行きません。ただソフトウェア全体の技術が膨大なのと同様にハードウェア技術全体はものすごい大きさを持っています。この壁を低くできると IoT や FA の世界がより豊かになるでしょう。そのツールの一つが Nerves/Elixir であると考えてます。

さて、Nerves 以外にもハードウェアの厚い壁をどうにかする方法が欲しくなります。

- ややこしいアクセスをさせずに API でアクセスできれば
- 特定のハードウェアデバイスをアクセスするのに統一された名前付け、少なくともユニークな番号がついていて欲しい

この要求をもう少し現在の技術で具体的にしたのが以下です。

- すべてのデバイスが IP アドレスを持ってる
- TCP/UDP 上の API でアクセス可能

こうして GPIO も $I^2C$ も SPI も UART も見えない世界ならソフトウェアエンジニアはかなり入り込みやすくなります。あと、ハードウェアがよりユニット化して扱いやすく慣れば、システム全体を組み上げるのも相当楽になるでしょう。

- すべてのデバイスが無線通信でアクセスできる
- すべてのデバイスがワイヤレス給電ないしは絵ネジーハーベスティングする

今回はこれを実現する未来を想像するためにプロトタイプを作成してみました。


# 基本方針
上に書いたコンセプトから、ざっくり以下のような方針で仕様を作りました。

## 対象とするデバイス
まずデバイスはリレー（継電器）にしました。リレーは FA において最も基本的な素子です。継電器だけで AND-OR-NOT 等の論理演算を行うこともしますし、小さな電力の回路で大きな電力を制御するようないわば増幅機能や、出力から入力へ正帰還をかけて記憶機能としても使います。
なお、現在の FA はリレー自体は、増幅機能や、入力と出力との回路を電気的に絶縁する目的で使い、論理演算や記憶機能はPLCと呼ばれるリレーを模倣するコンピュータで実行するのが大多数です。

## 通信方法と制御方法
当然 over IP にはします。それより上の層については Nerves/Elixir で扱いやすい方法を使います。IP の下は無線接続にします。今どきなら local 5G でと言いたいところですが、とにかくさっさと実現するという方針で IEEE802.11g の WiFi にします。

## 給電
Wireless 給電にしたいところですが、ハードルが若干高いので、ここは有線で給電します。ただし、必要な種類を全部供給するのではなく、1系統の給電で全部を賄うということにします。

# 設計と実装

## シングルボードコンピュータ (SBC)
安定して Nerves が動く WiFi 付きのRaspberry Pi 0WH を選択しました。ギリギリ Lichee Nano で動くという噂もあったのですが、安定性・再現性・保守性の観点で選択しませんでした。以下の写真は Lichee Nanoとラズパイ0Wの大きさの比較です。なお、左上はLichee Nanoのワイヤレスモジュールです。

![IMG_0257.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260320/ddcdd69c-7835-c49d-9ba7-2388c7dee5f8.jpeg "Lichee Nano（左上はワイヤレスモジュール）とラズパイ0W")

## 回路
回路は以下で設計しました。

- DC24V単一電源
- GPIO出力をフォトカプラを通してDC24Vでリレーを駆動
- ラズパイの電源 DC5V は、DC24V から DC-DC コンバータで降圧
- パイロットランプのLED付き
- リレーの駆動コイルのOFF時の逆起電力はダイオードで吸収

![IMG_0258.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260320/74e5a91e-d544-2790-fb9a-287fc52c6184.jpeg)

### リレー駆動
フォトカプラICは222Aを用いてます。ラズパイの GPIO 出力でフォトカプラとパイロットランプとの両方のLEDを直列で点灯します。並列にしないで直列にしたのは、電流制限抵抗をケチるのと消費電力をケチるのと両方でしょうが、どうもはっきり作ったときの意図をオボていません。自分で基板に回路を作ったっぽいのですがメモが何も残ってない。だれか、これ、私が作ったのかどうか知りませんか？

![IMG_0228.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260320/eb8600d3-1fe0-1954-c446-fa7dc1226685.jpeg)


### DC-DC コンバータ
電源をDC24Vに限定したので、ラズパイの電源はDC-DCコンバータで作ることになります。ここでは[StrawberryLinux](https://strawberry-linux.com/)さんの[LTC3130 MPPC付600mA昇降圧DC-DCコンバータ](https://strawberry-linux.com/catalog/items?code=13130)を使いました。

![IMG_0237.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260320/2671a41e-9ce7-cb97-b7c9-6c58c837da8a.jpeg)

### リレー本体
これらの部品も含めてまとめて一つのユニットにしないといけません。でっかいリレーを使えばなんとか入るのではないかと思いましたが、ガタイがデカイのは価格も高いです。新品使うのはアホくさいのでヤフオクで探しました。コイルがAC100VやDC110Vが多くてDC24Vのがなかなかない。あと、リレー本体だけでなく、リレーを保持するソケットも必要です。ようやくOMRONのMM2XPとMM4XPというパワーリレーを入手しました。以下の写真で左が MM4XPで右がMM2XPです。

![IMG_0240.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260320/3e1ed9de-9e40-a785-8f5e-e14ed3756183.jpeg)

ただしリレーのケースの中はコイルと接点がどんとあってそんなに無駄なスペースがあるわけではありません。ラズパイ0Wでもこのリレーの隙間には大きすぎて入りません。そこで、MM4XPのケースに一回り小さなMM2XPの本体部分を 移植するということをしました。

ちなみに私がゲットしたリレーで最大のものはラッチリレーというやつです。通常のリレーはコイルに電流を流すと接点ONになるので、ONにしてる間はずっとコイルが電力を消費します。ラッチリレーはONとOFFとの切り替わり時にコイルに電流を流せば、あとは状態を保持するリレーです。フリップフロップのような動作ですが、ラッチリレーは物理的に安定な2状態を持ってて、状態は物理的に保持します。

![IMG_0245.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260320/5b475f85-1760-7943-ed04-c37dbc30efbb.jpeg)

### 組み立て
以上のパーツを組み立てます。以下がパーツを全部揃えた写真です。上から右回りに…

- リレーのケース
- リレーのソケット
- AC100V → DC24VのACアダプタ
- 電子パーツ類（左から）
  - サージ保護ダイオード（おそらくそのへんに転がってたショットキーバリアダイオード）
  - DC-DCコンバータ基板
  - フォトカプラ基板
- ラズパイ0WH
- MM2XPの中身を取り付けたMM4XPの台の部分

![IMG_0249.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260320/92793f68-f833-86a2-0b6c-d2002dc66b55.jpeg)

さあこれを一体化します。
次の写真は回路構成を無視してとにかくリレーのケースに全部を詰め込んだらどうなったかというものです。

![IMG_0250.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260320/ac3e8af6-425f-fd94-5bf0-2f4f3557b176.jpeg)

ちゃんと配線までしてこれをきちんと詰め込むのはなかなか骨でした。完成品がこちら。

![IMG_0255.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260320/6f89a5dc-446b-698f-d473-248906056fa6.jpeg)

## ファームウェア
Nerves の RPi0 用のファームウェアを SDカードに焼いて、ラズパイ0WHに挿します。するとこれは、DC24Vをかけると動作するNervesマシンで、WiFi経由で通信して、パワーリレーを駆動する「すまあとリレー」になります。

# 動作
WiFi経由でNervesがつながるので、ssh でログインして GPIO を叩けば、それだけでリレーがいごきます。今回は GPIO の26にフォトカプラをつなぎましたので、それを `Circuits.GPIO` で `open` して 0 か 1 かを `write` すればリレーがいごきます。

## 連続して ON/OFF
連続的にリレーをカチカチ言わせてLEDを光らせるならこんな風にプログラムを書けばオケです。`Relay.repeat(100, 10)` とやるとOn/Off時間が100msで10回のOn/Offをします。

```exs
defmodule Relay do
  require Logger

  def repeat(interval, times) do
    {:ok, ref} = Circuits.GPIO.open(26, :output)
    repeat_aux(ref, interval, times)
    Circuits.GPIO.close(ref)
  end

  defp repeat_aux(_ref, _ms, 0) do
    Logger.debug("#{__MODULE__}: end repeat")
  end

  defp repeat_aux(ref, ms, n) do
    Circuits.GPIO.write(ref, 1)
    Process.sleep(ms)
    Circuits.GPIO.write(ref, 0)
    Process.sleep(ms)
    repeat_aux(ref, ms, n-1)
  end
end
```

## アナログ電話サウンドシミュレータ
カチカチを続けてやると昔ながらの電話のダイヤル音を聞いているようです。以下のプログラムで `Relay.tel_sound(電話番号の文字列, PPS値)` で電話番号に応じたダイヤルパルス音がします。PPS は Pulse per Second で 10 とか 20 とかを入れてください。

```exs
  def tel_sound(telno_str, pps) do
    pause = pps * 25
    delay = div(1000, pps*2)
    String.split(telno_str, "", trim: :true)
    |> Enum.map(&(String.to_integer(&1))) 
    |> Enum.each(&(tel_sound_num(delay, &1, pause)))
  end

  def tel_sound_num(delay, num, pause) do
    case num do
      0 -> repeat(delay, 10)
      _ -> repeat(delay, num)
    end
    Process.sleep(pause)
  end
```

いごかしているようすがこちら。ちなみにこれは私の所属している大学の代表番号を20PPSで鳴らしています。

https://twitter.com/kikuyuta/status/1375775134586335233

## 多数のデバイスをネットワーク上で
今回はやりませんでしたが、ベースが Nerves ですから、この「すまあとリレー」が大量にあっても、IPネットワーク上にあって互いにネットワーク到達性があるなら `Node.connect` してやればリレー全体を一様な空間でIPアドレスでアクセスするのは容易です。
そしてリレー以外も同様にIPを指定して特定の関数でアクセスできれば、システムを用意に構築できそうに思えますよね。

# まとめ
リレーを一つの Wireless Nerves ホストにしてみました。
これはでかくてぶかっこうですが、すべてのハードウェアのデバイスを単位ごとにモジュール化し API でアクセスできるようになれば、多くのエンジニアが IoT や FA の世界で活躍できることになるでしょう。

さて、明日の [#NervesJP Advent Calendar 2021](https://qiita.com/advent-calendar/2020/nervesjp) の記事も [@torifukukaiou](https://qiita.com/torifukukaiou) さんの [【Elixir】rclexから始めるROS 2 (2021/12/05)](https://qiita.com/torifukukaiou/items/9802f2f26f15778679d7) です。お楽しみに！

# 謝辞
- これは [FA設備技術勉強会](https://fa-study.connpass.com) の[第４回FA設備技術勉強会](https://twitter.com/kikuyuta/status/1375775134586335233)で初お披露でした。発表の機会を頂いてありがとうございました。
- リレーの改造時に [kochi.ex](https://kochi-ex.connpass.com) の @32hero さんにお手伝いをしてもらいました。ありがとうございます。


# 参考文献
[デバイスごとにアドレスとAPIを 〜プロトタイプを Elixir/Nerve で〜](https://www.slideshare.net/YutakaKikuchi1/api-elixirnerves)