1.リレーモジュール
リレーモジュールとして「2 CHANNEL 5V 10A RELAY MODULE」を使うことにしました。以前に購入したセンサーモジュールセットに入っていたものですが、単体ならAmazonで200円前後で入手できると思います。
![](photo/Relay1.jpg)
左写真のように5Vの2チャネル・リレー・インタフェース・ボードで、VCCはESP32の5V(VINピン)から供給し、GNDどうしをつないで使用します。IN1とIN2には、それぞれESP32の作動用GPIOピンを接続します。
このリレーは負論理で動作します。IN1、IN2の各ピンはLOW信号を送信するとオンになり、HIGH信号を送信するとオフになります。
![](photo/Relay2.jpg)
リレーの高圧側にはCOM(コモン)、NC(ノーマルクローズ)、NO(ノーマルオープン)の3つの端子が2セットあります。それぞれ、COMとNOまたはCOMとNCの間で回路を構成します。
NCはリレーが動作していない状態で閉じていて、動作すると開きます。NOはその逆になります。ここではリレーが動作した時に回路を閉じたいので、NOを使うことにします。
高圧側は、AC250V 10AあるいはDC30V 10Aに対応しています。モーターなど始動電流が大きいもの(定格の5~7倍になる)には注意が必要ですが、かなり大電流に対応できます。
2.リレーと電気回路の配線
リレーモジュールは監視用デバイスに組み込むことにします。リレーの制御側はESP32の該当ピンと下図のように配線します。
![](photo/RelayCircuit1.jpg)
〔リレー制御側〕
・リレー側VCC(赤線) → ESP32側VIN(5V)
・リレー側IN1(緑線) → ESP32側GPIO26
・リレー側IN2(青線) → ESP32側GPIO27
・リレー側GND(黒線) → ESP32側GND
リレーの接点側には商用電源と電球などを接続できますが、今回は電池と豆電球で通電確認を行います。ホームセンターへ出かけて、写真のような小物を買ってきました。
![](photo/RelayParts.jpg)
接点側の配線は次のようにします。
![](photo/RelayCircuit2.jpg)
〔リレー1接点側〕
・リレー側COM(赤線) → 電池マイナス
・リレー側NO (黒線) → 赤色豆電球
・赤色豆電球 (黒線) → 電池プラス
〔リレー2接点側〕
・リレー側COM(黄線) → 電池マイナス
・リレー側NO (黒線) → 緑色豆電球
・緑色豆電球 (黒線) → 電池プラス
3.コードの解説
①ヘッダーファイルとデータ等の定義
・28~29行 環境に合った情報を設定してください!
・36~39行 リレーのピン番号とON/OFF状態保持用です。
・70~73行 2回路のために2つのボタンと状態表示を配置しています。
#include <Arduino.h>
#include <WiFi.h>
/* Function Prototype */
void doInitialize();
void httpListen();
void httpRequestProccess(String*);
void httpSendResponse(WiFiClient*);
void connectToWifi();
/* 基本属性定義 */
#define SPI_SPEED 115200 // SPI通信速度
// ルーター接続情報
#define WIFI_SSID "your_ssid"
#define WIFI_PASSWORD "your_password"
#define HTTP_PORT 80
// Webサーバーオブジェクト
WiFiServer server(HTTP_PORT);
// リレー制御情報
const int relay1Pin = 26;
const int relay2Pin = 27;
String relay1State = "OFF";
String relay2State = "OFF";
/* HTMLレスポンスヘッダー */
const String strResponseHeader = "HTTP/1.1 200 OK\r\nContent-Type:text/html\r\n"
"Connection:close\r\n\r\n";
/* HTMLページ構成要素 */
// (ページヘッダー部)
const String strHtmlHeader = R"rawliteral(
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html { font-family: Helvetica; display: inline-block; margin: 0px auto;text-align: center;}
h1 {font-size:28px;}
.btn_on { padding:12px 30px; text-decoration:none; font-size:24px; background-color:
#668ad8; color: #FFF; border-bottom: solid 4px #627295; border-radius: 2px;}
.btn_on:active { -webkit-transform: translateY(0px); transform: translateY(0px);
border-bottom: none;}
.btn_off { background-color: #555555; border-bottom: solid 4px #333333;}
.slider { width: 200px;}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
</head>
)rawliteral";
// (ページボディー部)
const String strHtmlBody = R"rawliteral(
<body><h1>%PAGE_TITLE%</h1>
<p>SW-1 State : %RELAY1_STATE%</p>
<p>%BUTTON1_STATE%</p>
<p>SW-2 State : %RELAY2_STATE%</p>
<p>%BUTTON2_STATE%</p>
</body>
</html>
)rawliteral";
// (ON/OFFボタン)
const String strButton1On = R"rawliteral(
<a href="/ON1"><button class="btn_on"> ON </button></a> )rawliteral";
const String strButton1Off = R"rawliteral(
<a href="/OFF1"><button class="btn_on btn_off">OFF</button></a> )rawliteral";
const String strButton2On = R"rawliteral(
<a href="/ON2"><button class="btn_on"> ON </button></a> )rawliteral";
const String strButton2Off = R"rawliteral(
<a href="/OFF2"><button class="btn_on btn_off">OFF</button></a> )rawliteral";
②定型のsetup()とloop()
・setup()では初期化処理とルーター接続の関数を実行します。
・loop()では「HTTPリスン処理」を実行し続けます。
void setup() {
doInitialize(); // 初期化処理をして
connectToWifi(); // Wi-Fiルーターに接続する
}
void loop() {
httpListen(); // HTTP手順を制御する
}
③初期化処理
・シリアルポートを設定し、2回路のリレーをOFF(負論理でHIGH)状態に設定します。
void doInitialize() {
Serial.begin(SPI_SPEED);
pinMode(relay1Pin, OUTPUT);
pinMode(relay2Pin, OUTPUT);
digitalWrite(relay1Pin, HIGH);
digitalWrite(relay2Pin, HIGH);
}
④HTTPリスン処理
・「HTTPリスン定型処理」です。
・125行目 「HTTPリクエスト処理」を実行します。
・126行目 「HTTPレスポンス処理」を実行します。
void httpListen() {
String strBuffer = "";
WiFiClient client = server.available();
if (client) { // クライアントから着信があれば
Serial.println("New Client.");
String currentLine = "";
while (client.connected()) { // 接続中に以下を繰り返す
if (client.available()) { // 着信データがあれば
char c = client.read(); // 1バイト読み込んで
strBuffer += c; // 受信文を形成する
if (c == '\n') {
// 復帰改行文字で受信領域が空なら、リクエスト処理とレスポンス
// を送信してループを脱出する
if (currentLine.length() == 0) {
httpRequestProccess(&strBuffer);
httpSendResponse(&client);
break;
} else { // 後続文字があればラインバッファをクリア
currentLine = "";
}
} else if (c != '\r') { // 改行以外ならラインバッファに結合する
currentLine += c;
}
}
}
// コネクションを閉じる
client.stop();
Serial.println("Client disconnected.\n");
}
}
⑤HTTPリクエスト処理
・リクエストメッセージを解析してアクションを起こします。
・145~153行目 リレー1のON/OFFボタンのクリックによりリレー1を設定(負論理)します。
・154~162行目 リレー2のON/OFFボタンのクリックによりリレー2を設定(負論理)します。
void httpRequestProccess(String* strbuf) {
// 受信メッセージを判別してリレーをオン/オフする
if (strbuf->indexOf("GET /ON1") >= 0) {
Serial.println("GET /ON1");
relay1State = "ON";
digitalWrite(relay1Pin, LOW);
} else if (strbuf->indexOf("GET /OFF1") >= 0) {
Serial.println("GET /OFF1");
relay1State = "OFF";
digitalWrite(relay1Pin, HIGH);
}
if (strbuf->indexOf("GET /ON2") >= 0) {
Serial.println("GET /ON2");
relay2State = "ON";
digitalWrite(relay2Pin, LOW);
} else if (strbuf->indexOf("GET /OFF2") >= 0) {
Serial.println("GET /OFF2");
relay2State = "OFF";
digitalWrite(relay2Pin, HIGH);
}
Serial.print("Switch1 State: "); Serial.println(relay1State);
Serial.print("Switch2 State: "); Serial.println(relay2State);
}
⑦HTTPレスポンス処理
・HTTPレスポンスヘッダー、ページヘッダー、ボディー部を順番にクライアントへ送信します。
・176行 編集のために、生の文字リテラルで定義したボディー部を作業エリアに取り込みます。
・181,186行 クリックされているボタンによって次のボタン表示を設定します。
void httpSendResponse(WiFiClient* client) {
// HTTPレスポンスヘッダーを送信
client->println(strResponseHeader);
// ページヘッダーを送信
client->println(strHtmlHeader);
// ページボディー部を編集して送信
String buf = strHtmlBody;
buf.replace("%PAGE_TITLE%", "Web Switch");
buf.replace("%RELAY1_STATE%", relay1State);
buf.replace("%RELAY2_STATE%", relay2State);
//(ON/OFFボタンの設定)
if (relay1State == "OFF") {
buf.replace("%BUTTON1_STATE%", strButton1On);
} else {
buf.replace("%BUTTON1_STATE%", strButton1Off);
}
if (relay2State == "OFF") {
buf.replace("%BUTTON2_STATE%", strButton2On);
} else {
buf.replace("%BUTTON2_STATE%", strButton2Off);
}
client->println(buf);
client->println(); // 最後に、HTTP終端の空行を送信
}
⑧コネクション確立用
・以前と同じ定型のロジックです。
/* Wi-Fiルーターに接続する */
void connectToWifi() {
// Wi-Fi接続して
Serial.print("Connecting to Wi-Fi ");
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
// モニターにローカル IPアドレスを表示する
Serial.println("WiFi connected.");
Serial.print(" *IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}
4.動作検証
シリアルモニターを立ち上げて実行を開始します。Wi-Fi接続が完了すると「割り当てられたIPアドレス」が表示されます(ここでは192.168.0.30)。
パソコンまたはスマートフォンのWebブラウザーを起動して、アドレスバーに「割り当てられたIPアドレス」を入力すると次のような状態の「Web Switch」画面(写真上)が表示され、2つの豆電球は消灯しています。
・SW1 State: OFF
・[ON]ボタン
・SW2 State: OFF
・[ON]ボタン
上の[ON]ボタンをクリックまたはタップすると[OFF]ボタンに反転し、写真・中のように赤色豆電球が点灯して、SW1 Stateが「ON」に変わります。
続いて下の[ON]ボタンをクリックまたはタップすると、同様に[OFF]ボタンに反転し、図右・下のように緑色豆電球が点灯して、SW2 Stateが「ON」に変わります(写真下)。
これらの過程はシリアルモニターに図左のように表示されます。
![](photo/RelayModuleR4.jpg)
![](photo/RelayModuleR1.jpg)
![](photo/RelayModuleR2.jpg)
![](photo/RelayModuleR3.jpg)
今回も定型化したHTTPリスンメソッドをベースにしましたが、このようにして簡単にリレーモジュールを制御できることがわかりました。スマートホームの一部などに応用できそうです。
スケッチ全体は、ページ先頭の[Download]ボタンでダウンロードしてください。
ESP32をWebサーバーに仕立てたWi-Fi通信の実験はこれで終わりにします。
最終回では、開発したローカルサーバーを公開して、外部のどこからでも接続・制御できるようにします。
お楽しみに!