ESP8266とリレーモジュールでLED電球を制御する
リレーはコイルと、コイルに通電することで開閉する接点で構成されて、低入力電圧で高電圧デバイスを制御することができます。
リレーモジュールはリレー以外、LEDインジケータ、トランジスタ、還流ダイオードなどの電子部品も含まれて、制御が容易になります。
リレーモジュール pinout
次の図は 5V シングルチャンネルリレーモジュールです。
出力側の端子台には COM (common), NC (normally closed), NO (normally open) の 3 つの端子があります。
- COM 端子は共通端子。
- NC(常閉)端子は常に COM 端子に接続する。
- NO(常開)端子は常に開いているため、回路を閉じない限り COM に接続しない。
電球は長時間使用しないなので、NO 端子に接続します。
入力側は VCC、GND、IN の 3 つのピンがあります。 IN ピンは NodeMCU の GPIO ピンに接続し、リレーを制御します。active-low ピンなので、LOW にするとリレーが動作します。
NodeMCU V3 を使っているので、USB から給電する場合、5V リレーモジュールに電力を供給するには、VU (Vusb)
ピンを使用します。リレーモジュールの VCC ピンを VU ピンに接続します。
ちなみに一部のリレーモジュールは 3.3V ピンに接続しても動作します。
以下は 2 チャンネルリレーモジュールを使用した例です。
開発環境構築
非同期通信で電球の ON/OFF を制御したいため、ESPAsyncWebServer というライブラリを利用します。
私は PlatformIO IDE for VSCode を使っているので、platformio.ini
で lib_deps
パラメーターをこのライブラリ名に設定します。
[env:nodemcuv2]
platform = espressif8266
board = nodemcuv2
framework = arduino
board_build.f_cpu = 80000000L
upload_speed = 115200
monitor_speed = 115200
platform_packages =
platformio/framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git
lib_deps = ESP Async WebServer
コード
コードは以下となります。
PROGMEM
を使って、フラッシュメモリに HTML コードを保存します。
注意すべき点は、rawliteral
は 2 つの %
記号の間をプレースホルダーとして扱うので、
CSS コードでパーセントを指定する場合、%%
で %
をエスケープする必要があります。
例えば translate(-50%, -50%)
は translate(-50%%, -50%%)
になります。
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESPAsyncWebServer.h>
const char *ssid = "SSID";
const char *password = "PASSWORD";
const char *PARAM_STATE = "state";
const int relayPin = 5; // D1
boolean relayNO = true; // whether the relay is normally open
AsyncWebServer server(80);
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Control LED</title>
<style>
.center { position: absolute; top: 50%%; left: 50%%; transform: translate(-50%%, -50%%); }
.switch { position: relative; display: inline-block; width: 80px; height: 40px; }
.switch input { display: none; }
.slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #e0e0e0; border-radius: 40px; transition: 0.3s; }
.slider::before { position: absolute; content: ""; height: 30px; width: 30px; top: 5px; left: 5px; background-color: #fff; border-radius: 50%; transition: 0.3s; }
input:checked + .slider { background-color: #4caf50; }
input:focus + .slider { box-shadow: 0 0 1px #4caf50; }
input:checked + .slider:before { transform: translateX(40px); }
</style>
</head>
<body>
<div class="center">
<h3 id="title">%LED_STATE%</h3>
<label class="switch">
<input type="checkbox" onchange="toggleSwitch(this)" %IS_CHECKED%/>
<span class="slider"></span>
</label>
</div>
<script>
function toggleSwitch(e) {
const req = new XMLHttpRequest();
const title = document.getElementById("title");
if (e.checked) { req.open("GET", "/light?state=1", true); title.innerHTML = "LED Lamp ON"; }
else { req.open("GET", "/light?state=0", true); title.innerHTML = "LED Lamp OFF"; }
req.send();
}
</script>
</body>
</html>
)rawliteral";
String processor(const String &var) {
if (var == "LED_STATE") {
String stateVal;
if (relayNO) {
stateVal = (digitalRead(relayPin)) ? "OFF" : "ON";
} else {
stateVal = (digitalRead(relayPin)) ? "ON" : "OFF";
}
String stateText = "LED Lamp " + stateVal;
return stateText;
} else if (var == "IS_CHECKED") {
String checkbox;
if (relayNO) {
checkbox = (digitalRead(relayPin)) ? "" : "checked";
} else {
checkbox = (digitalRead(relayPin)) ? "checked" : "";
}
return checkbox;
}
return String();
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.printf("WiFi Failed!\n");
return;
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
pinMode(relayPin, OUTPUT);
if (relayNO) {
digitalWrite(relayPin, HIGH);
} else {
digitalWrite(relayPin, LOW);
}
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) {
request->send_P(200, "text/html", index_html, processor);
});
server.on("/light", HTTP_GET, [](AsyncWebServerRequest *request) {
String state;
if (request->hasParam(PARAM_STATE)) {
state = request->getParam(PARAM_STATE)->value();
if (relayNO) {
digitalWrite(relayPin, !state.toInt());
} else {
digitalWrite(relayPin, state.toInt());
}
} else {
state = "No state sent";
}
request->send(200, "text/plain", "[GET]: " + state);
});
server.begin();
}
void loop() {}
IP アドレスはシリアルモニターで出力されます。
ブラウザからこの IP アドレスにアクセスすると、トグルスイッチが画面に表示されます。このスイッチの ON/OFF で LED 電球を点滅できます。