ESP8266で家電(テレビ)を制御してみる
ESP8266という小型WIFIモジュールを使って、家電の電源制御ができたのでまとめて書いてみる。
国内で購入すると、割高なのでAliexpressで中国から購入。
(amazon.co.jpでも取り扱いがありますが国際便なので同じかも)



リレーモジュールはUSB電源5Vで動作するのであえて3.3Vに変換しなくても良い。
書き込みにはESP-01用のライターを使用しています。
今回はarduinoという開発ソフトを使用して、ESP8266に書き込むプログラムを作成。
littleFSにて、HTMLファイルをESP8266のファイル領域に書き込む。
Arduinoのセットアップについては、別記事を参照して下さい。
書き込み後にESP8266をリレーモジュールに挿して、USBで電源供給すると、WIFIの一覧にesp-xxxxxxが表示されるので、そのSSIDに接続します。
http://192.168.10.1を入力すると、ON/OFFの切替画面が表示される。
http://192.168.10.1/config/を入力すると、設定画面が表示されます。
WIFIクライアントとして接続する場合、接続先のSSID/パスワードを設定します。
DHCPでなく、固定IPで設定するほうが、直接IPアドレスを入力して制御できるので、おすすめです。
初回ログイン時は 認証が必要です。
ユーザ = root パスワード = root になっています
不要な場合は143-145行をコメントアウトしてください。
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h> //ESP Web Server Library
#include <LittleFS.h>
#include <Ticker.h>
#define SETTING_INI "/SETTING.txt"
#define RELAY_PIN 0 // GPIO0
#define RELAY_ON LOW
#define RELAY_OFF HIGH
//#define DEBUG_RELAY
#define SSBAUD 115200 // logger on console
ESP8266WebServer server( 80 );
Ticker ticker;
WiFiEventHandler NewClient;
// RELAY OUT STATUS
//bool IO_STATUS = RELAY_OFF;
// AP_MODE Use
String SOFTAP_SSID = "ESP-" + String(ESP.getChipId(), HEX);
String SOFTAP_PASS = "password"; // default
const String DEF_IP = "192.168.10.1"; //default
const String DEF_SN = "255.255.255.0";
// Basic authenticate
String www_username = "root";
String www_password = "root";
String XSSID = SOFTAP_SSID;
String XPASS = SOFTAP_PASS;
bool UseIP = true;
String ConIP = DEF_IP; //default
String ConSN = DEF_SN;
String ConGW = DEF_IP;
String CName = "Controller";
bool BotOf = false;
bool APMode = false;
String MDNSName = "ESP-";
void setup() {
IPAddress _ip, _gw, _sn;
int reConnect;
delay(100);
// Serial Port Open
Serial.begin(SSBAUD);
Serial.flush();
Serial.println("***BOOT-UP***");
pinMode(RELAY_PIN, OUTPUT);
// initialize file System
LittleFS.begin();
Serial.println("FileList-----------");
listDir( "/");
Serial.println("------------Done.");
bool FoundSetting = LoadConfig();
if (!FoundSetting || (FoundSetting && XSSID == SOFTAP_SSID)) {
APMode = true;
}
// Client Mode ---------------------------------
if (!APMode) {
WiFi.mode(WIFI_STA);
DebugInfo();
if (UseIP) {
_ip.fromString(ConIP);
_gw.fromString(ConGW);
_sn.fromString(ConSN);
WiFi.config(_ip, _gw, _sn);
}
delay(100);
Serial.println("Will be Connecting to WIFI..");
WiFi.begin(XSSID.c_str(), XPASS.c_str());
for (reConnect = 1; reConnect < 120 ; reConnect++) {
if (WiFi.status() == WL_CONNECTED) {
break;
}
delay(500); // 0.5s
Serial.println(".");
}
// 60 second time out
if (reConnect < 120) {
if (!UseIP) {
Serial.println("");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
Serial.print("Mac Address: ");
Serial.println(WiFi.macAddress());
Serial.print("Subnet Mask: ");
Serial.println(WiFi.subnetMask());
Serial.print("Gateway IP : ");
Serial.println(WiFi.gatewayIP());
Serial.print("DNS IP : ");
Serial.println(WiFi.dnsIP());
}
Serial.println("WiFi connected!");
RelayControl((BotOf ? RELAY_OFF : RELAY_ON)); // boot on power-on
} else {
wifi_disconnect();
Serial.println("Client Not Connect! Go AP Mode");
APMode = true;
}
}
// APMode --------------------------------------
if (APMode) {
WiFi.mode(WIFI_AP_STA);
XSSID = SOFTAP_SSID;
XPASS = SOFTAP_PASS;
ConIP = DEF_IP;
ConSN = DEF_SN;
ConGW = DEF_IP;
DebugInfo();
WiFi.softAP(SOFTAP_SSID.c_str(), SOFTAP_PASS.c_str());
_ip.fromString(ConIP);
_sn.fromString(ConSN);
WiFi.softAPConfig(_ip, _ip, _sn);
NewClient = WiFi.onSoftAPModeStationConnected(&WiFiStationConnected);
// IO_STATUS = RELAY_ON;
}
// URL Request --------------------------------
server.on("/config/", HTTP_POST, []() {
SaveConfig();
StreamWebSend("/config/");
ticker.attach(3, RestartReady);
});
server.on("/", HTTP_POST, []() {
PinOutControl();
});
server.on("/resetconfig", HTTP_GET, []() {
configReset();
});
server.onNotFound([]() {
if (server.hasArg("pin")) {
PinOutControlWithApp();
} else {
// BASIC authenticate
if (!server.authenticate(www_username.c_str(), www_password.c_str())) {
return server.requestAuthentication();
}
StreamWebSend(server.uri());
}
});
server.begin();
}
// Manage incoming device connection on ESP access point
void WiFiStationConnected(const WiFiEventSoftAPModeStationConnected& sta_info) {
Serial.println("New Client is " + macToString(sta_info.mac));
}
String macToString(const unsigned char* mac) {
char buf[20];
snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
return String(buf);
}
// Serial Out Config
void DebugInfo() {
if (APMode) {
Serial.println("[AP SETTING MODE]");
} else {
if (UseIP) {
Serial.println("[DYNAMIC IP MODE]");
} else {
Serial.println("[STATIC IP MODE]");
}
}
Serial.println("SSID: " + XSSID);
Serial.println("PASS: " + XPASS);
Serial.println("MAC : " + WiFi.macAddress());
if (UseIP) {
Serial.println("IP : " + ConIP);
Serial.println("MASK: " + ConSN);
Serial.println("GW : " + ConGW);
}
}
// load config file
bool LoadConfig() {
File f = LittleFS.open(SETTING_INI, "r");
delay(100);
if (f) {
String BWork;
XSSID = f.readStringUntil('\n'); XSSID.trim(); // SSID
XPASS = f.readStringUntil('\n'); XPASS.trim(); // PASS
BWork = f.readStringUntil('\n'); BWork.trim(); // IP-MODE(true=static/false=DHCP)
UseIP = ( BWork == "true" ? true : false);
ConIP = f.readStringUntil('\n'); ConIP.trim(); // IP-ADRS
ConSN = f.readStringUntil('\n'); ConSN.trim(); // IP-MASK
ConGW = f.readStringUntil('\n'); ConGW.trim(); // IP-GWAY
CName = f.readStringUntil('\n'); CName.trim(); // Control name
if (CName.length() == 0) {
CName = "Controler";
}
BWork = f.readStringUntil('\n'); BWork.trim(); // Booting On Power On Relay
BotOf = ( BWork == "true" ? true : false);
f.close();
Serial.println("Config Loaded!");
return true;
}
return false;
}
// save config file
void SaveConfig() {
Serial.println("SaveConfig");
XSSID = server.arg("ssid");
XPASS = server.arg("pass");
UseIP = (server.arg("useip") == "true" ? true : false);
ConIP = server.arg("ip");
ConSN = server.arg("mask");
ConGW = server.arg("gway");
CName = server.arg("cnam");
BotOf = (server.arg("btof") == "true" ? true : false);
File f = LittleFS.open(SETTING_INI, "w");
delay(100);
f.println(XSSID);
f.println(XPASS);
f.println(UseIP ? "true" : "false");
f.println(ConIP);
f.println(ConSN);
f.println(ConGW);
f.println(CName);
f.println(BotOf ? "true" : "false");
f.close();
}
void PinOutControl() {
// PIO control
if (server.hasArg(CName)) {
if (server.arg(CName) == "1") {
Serial.println(CName + ":ON");
RelayControl(RELAY_ON);
} else {
Serial.println(CName + ":OFF");
RelayControl(RELAY_OFF);
}
}
StreamWebSend("/");
}
void PinOutControlWithApp() {
// PIO control
if (server.hasArg("pin")) {
if (server.arg("pin") == "ON") {
Serial.println(CName + ":ON");
RelayControl(RELAY_ON);
} else if (server.arg("pin") == "OFF") {
Serial.println(CName + ":OFF");
RelayControl(RELAY_OFF);
}
String RELAY_STATUS = (digitalRead(RELAY_PIN) == RELAY_ON) ? "ON" : "OFF";
server.send(200, "text/plain", RELAY_STATUS );
} else {
}
}
void listDir(const char *dirname) {
Dir root = LittleFS.openDir(dirname);
while (root.next()) {
String TargetPath = String(dirname) + '/' + String(root.fileName());
if (root.isDirectory()) {
listDir(TargetPath.c_str());
}else{
File file = root.openFile("r");
Serial.print(" FILE: ");
Serial.print(TargetPath);
Serial.print(" SIZE: ");
Serial.print(file.size());
time_t t = file.getLastWrite();
struct tm * tmstruct = localtime(&t);
file.close();
Serial.printf(" LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec);
}
}
}
void RelayControl(int sw) {
#ifdef DEBUG_RELAY
SerialOut("GPIO(" + String(RELAY_PIN) + ") is " + ((sw == RELAY_ON) ? "on" : "off"));
#else
digitalWrite(RELAY_PIN, sw);
#endif
// IO_STATUS = sw;
}
// MIMEtype
String getContentType(String filename) {
if (filename.endsWith(".html") || filename.endsWith(".htm")) return "text/html";
else if (filename.endsWith(".css")) return "text/css";
else if (filename.endsWith(".js" )) return "application/javascript";
else if (filename.endsWith(".png")) return "image/png";
else if (filename.endsWith(".gif")) return "image/gif";
else if (filename.endsWith(".jpg")) return "image/jpeg";
else return "text/plain";
}
// open and send LittleFS file( and parameter)
bool StreamWebSend(String path) {
// settings
if (path == "/config/setting.js") {
String SettingInfo, UIP, BTOF;
UIP = UseIP ? "true" : "false";
BTOF = BotOf ? "true" : "false";
SettingInfo = "var Setting = {SSID:'" + XSSID + "',PASS:'" + XPASS + "',UseIP:" + UIP + ",ConIP:'" + ConIP + "',ConSN:'" + ConSN + "',ConGW:'" + ConGW + "',CName:'" + CName + "',BtOf:" + BTOF + "};";
server.send(200, "application/javascript", SettingInfo);
return true;
}
// port info
if (path == "/status.js") {
String PortInfo, PTS;
PTS = ( digitalRead(RELAY_PIN) == RELAY_ON) ? "1" : "0";
// PTS = (IO_STATUS == RELAY_ON) ? "1" : "0";
PortInfo = "var Ports = " + PTS + ";" + "var CName = '" + CName + "';";
server.send(200, "application/javascript", PortInfo);
return true;
}
// load add path
if (path.endsWith("/")) {
path += "index.html";
}
// send file
if (LittleFS.exists(path)) {
String contentType = getContentType(path);
Serial.println("Sending..[" + path + "]");
File file = LittleFS.open(path, "r");
size_t send = server.streamFile(file, contentType);
file.close();
return true;
}
// not found
Serial.println("404 not found " + path);
server.send(404, "text/plain", "404 Page Not Found");
}
void configReset() {
LittleFS.remove(SETTING_INI);
delay(200);
Serial.println("remove Setting File");
RestartReady();
}
// restart Hooker
void RestartReady() {
wifi_disconnect();
LittleFS.end();
delay(100);
server.close();
delay(100);
ESP.reset();
ESP.restart();
}
void wifi_disconnect() {
while (WiFi.status() == WL_CONNECTED ) {
WiFi.disconnect();
delay(1000);
}
}
void loop() {
server.handleClient();
}
■arduino のソースコード ESPCONTROLER_src esp8266_src