ESP32からSolid State Relayを制御する

とある目的で、プログラムから複数の100VのON/OFFを制御したかったので、以下のSolid State Relayを購入しました。

[4 Channels Solid State Relay Module(Low Trigger)](https://www.cytron.io/p-4-channels-solid-state-relay-module-low-trigger)
![DSC_0005.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130771/ae4c2f82-b773-8fe5-06aa-d6960be90bc0.jpeg)
100Vを4チャンネル制御でき、信号も3.3V～5Vで送れるので、ArduinoやRaspberry Piで使えそうです。

ラッキーなことに、このリレーをArduinoから制御する[動画](https://www.youtube.com/watch?v=Rfaz5qOLKYQ)がありましたので、これを参考に実装を進めていきます。

ただ、Webブラウザから制御したかったので、ArduinoではなくESP32で実装します。

# 材料

- ESP32-DevKitC
- 4 Channels Solid State Relay Module
- ブレッドボード
- ケーブル（オス－メス） 

このほかに、100VでON/OFFしたい機器も用意します。
（今は内緒なので、今回は公開しません）

また、開発はArduino IDEで行います。

# 注意事項
100Vの交流を扱うので、触るときには気をつけるとともに、電気を通さない板の上などで作業を行うようにしてください。

# 接続
ESP32とリレーは以下のように接続します。

| ESP32 | リレー |
|:---:|:---:|
| 5V | DC+ |
| GND | DC- |
| IO4 | CH1 |
| IO5 | CH2 |
| IO16 | CH3 |
| IO17 | CH4 |

今回はとりあえず2チャンネルだけ使用します。
![DSC_0006.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130771/24a703d7-b38c-d311-738b-b0aace30780b.jpeg)
※ケーブルが全部黒で、わかりづらく、申し訳ないです．．．

# プログラム
ベースは[こちら](https://qiita.com/mine820/items/01d5b7dbf65296c6f6e8)と同じになります。
違う部分だけ書いていきます。

## 4チャンネル対応
4チャンネル分のポート番号を設定しておきます。

```c
int ssr[] = [4, 5, 16, 17];
int triggerType = LOW;
int ssrON, ssrOFF;
```

モジュールにより、リレーのON/OFFが逆になっていることがあり、Arduino UNOの場合はON/OFFがHIGH/LOWでいいのですが、ESP32の場合は逆にする必要があります。

そして初期化時等では、4チャンネルに対して処理を行っていきます。

```c
    for (i=0; i < 4; i++)
    {
        pinMode(ssr[i], OUTPUT);
        digitalWrite(ssr[i], ssrOFF);
    }
```

初期状態として、すべてのリレーをOFFにしておきます。

URLのパラメータとして、どのチャンネルをON/OFFするかを指定させます。

```c
    server.on("/", handleRoot);
    server.on("/relay", handleRelay);
    server.onNotFound(handleNotFound);
```

最初に表示するHTMLも4チャンネル対応にします。

```c
    html += "<li>リレー1を<a href=\"/relay?c=0&s=on\">ON</a>／<a href=\"/relay?c=0&s=off\">OFF</a>にする</li>";
    html += "<li>リレー2を<a href=\"/relay?c=1&s=on\">ON</a>／<a href=\"/relay?c=1&s=off\">OFF</a>にする</li>";
    html += "<li>リレー3を<a href=\"/relay?c=2&s=on\">ON</a>／<a href=\"/relay?c=2&s=off\">OFF</a>にする</li>";
    html += "<li>リレー4を<a href=\"/relay?c=3&s=on\">ON</a>／<a href=\"/relay?c=3&s=off\">OFF</a>にする</li>";
```

制御処理も、リンクのパラメータの指定に従って設定するように修正します。

```c
void handleRelay(void)
{
    String msg;
    int c = -1;
    int s = -1;

    // 「/relay?c=○&s=□」のパラメータが指定されているかどうかを確認
    if (server.hasArg("c") && server.hasArg("s"))
    {
        // 「○」の値に応じて、リレーのチャンネルを設定する
        msg = "リレー";
        if (server.arg("c").equals("0"))
        {
            c = 0;
            msg += "1";
        }
        else if (server.arg("c").equals("1"))
        {
            c = 1;
            msg += "2";
        }
        else if (server.arg("c").equals("2"))
        {
            c = 2;
            msg += "3";
        }
        else if (server.arg("c").equals("3"))
        {
            c = 3;
            msg += "4";
        }
        msg += "を";

        // 「□」の値に応じて、リレーのON/OFFを設定する
        if (server.arg("s").equals("on"))
        {
            s = ssrON;
            msg += "ON";
        }
        else if (server.arg("s").equals("off"))
        {
            s = ssrOFF;
            msg += "OFF";
        }
        msg += "にしました";

        // 設定
        if ((c >= 0) && (s >= 0))
        {
            digitalWrite(ssr[c], s);
        }
        else
        {
            msg = "パラメータが正しく指定されていません";
        }
   }

    // 変数msgの文字列を送信する
    server.send(200, "text/plain; charset=utf-8", msg);
}
```

# 実行
制御されている100Vの機器のほうは、今はまだ見せられないのですが、一応ちゃんと動いています。

起動時のシリアル出力です。
このIPアドレスにWebブラウザからアクセスします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130771/02288cdb-3d25-7859-a0c7-d797109efcb4.png)

Webブラウザに表示された画面です。
リレーのそれぞれのチャンネルにON/OFFを指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130771/7b181d1a-6497-1359-b14f-30f5d5287b7c.png)

チャンネル1をONにした状態です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130771/3c71270b-50d5-a4d7-f191-7c997a86293f.png)

LEDもチャンネル1だけONになっています。
![DSC_0007.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/130771/074339b1-965b-2d6c-8334-744a4b1adfd1.jpeg)

# ソースコード

```c
#include <WiFi.h>
#include <WebServer.h>

WebServer server(80);

// WiFi情報
const char* ssid = "wifi_ssid";
const char* pass = "wifi_pass";

// 使用ピン
int ssr[] = {4, 5, 16, 17};
int triggerType = LOW;
int ssrON, ssrOFF;

// 初期画面
void handleRoot(void)
{
    String html;

    // HTMLを組み立てる
    html = "<!DOCTYPE html>";
    html += "<html>";
    html += "<head>";
    html += "<meta charset=\"utf-8\">";
    html += "<title>リレーをON/OFFする</title>";
    html += "</head>";
    html += "<body>";
    html += "<p>リンクをクリックするとリレーがON/OFFします</p>";
    html += "<ul>";
    html += "<li>リレー1を<a href=\"/relay?c=0&s=on\">ON</a>／<a href=\"/relay?c=0&s=off\">OFF</a>にする</li>";
    html += "<li>リレー2を<a href=\"/relay?c=1&s=on\">ON</a>／<a href=\"/relay?c=1&s=off\">OFF</a>にする</li>";
    html += "<li>リレー3を<a href=\"/relay?c=2&s=on\">ON</a>／<a href=\"/relay?c=2&s=off\">OFF</a>にする</li>";
    html += "<li>リレー4を<a href=\"/relay?c=3&s=on\">ON</a>／<a href=\"/relay?c=3&s=off\">OFF</a>にする</li>";
    html += "</ul>";
    html += "</body>";
    html += "</html>";

    // HTMLを出力する
    server.send(200, "text/html", html);
}

// リレーのON/OFF
void handleRelay(void)
{
    String msg;
    int c = -1;
    int s = -1;

    // 「/relay?c=○&s=□」のパラメータが指定されているかどうかを確認
    if (server.hasArg("c") && server.hasArg("s"))
    {
        // 「○」の値に応じて、リレーのチャンネルを設定する
        msg = "リレー";
        if (server.arg("c").equals("0"))
        {
            c = 0;
            msg += "1";
        }
        else if (server.arg("c").equals("1"))
        {
            c = 1;
            msg += "2";
        }
        else if (server.arg("c").equals("2"))
        {
            c = 2;
            msg += "3";
        }
        else if (server.arg("c").equals("3"))
        {
            c = 3;
            msg += "4";
        }
        msg += "を";

        // 「□」の値に応じて、リレーのON/OFFを設定する
        if (server.arg("s").equals("on"))
        {
            s = ssrON;
            msg += "ON";
        }
        else if (server.arg("s").equals("off"))
        {
            s = ssrOFF;
            msg += "OFF";
        }
        msg += "にしました";

        // 設定
        if ((c >= 0) && (s >= 0))
        {
            digitalWrite(ssr[c], s);
        }
        else
        {
            msg = "パラメータが正しく指定されていません";
        }
   }

    // 変数msgの文字列を送信する
    server.send(200, "text/plain; charset=utf-8", msg);
}

// 存在しないアドレスが指定された時の処理
void handleNotFound(void)
{
    server.send(404, "text/plain", "Not Found.");
}

// 初期化
void setup()
{
    int i;
    
    // シリアルポートの初期化
    Serial.begin(115200);

    // WiFiのアクセスポイントに接続
    WiFi.begin(ssid, pass);
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
    }
    // ESP32のIPアドレスを出力
    Serial.println("WiFi Connected.");
    Serial.print("IP = ");
    Serial.println(WiFi.localIP());
    
    // Groveセンサーの初期化
    if (triggerType)
    {
        ssrON = HIGH;
        ssrOFF = LOW;
    }
    else
    {
        ssrON = LOW;
        ssrOFF = HIGH;
    }
    for (i=0; i < 4; i++)
    {
        pinMode(ssr[i], OUTPUT);
        digitalWrite(ssr[i], ssrOFF);
    }

    // 処理するアドレスを定義
    server.on("/", handleRoot);
    server.on("/relay", handleRelay);
    server.onNotFound(handleNotFound);
    // Webサーバーを起動
    server.begin();
}

// 処理ループ
void loop()
{
    server.handleClient();
}
```

# 最後に
このリレーで何を制御しようとしているのかは、そのうちどこかで発表した後に記事にします。