Skip to content

Instantly share code, notes, and snippets.

@laacz
Last active January 9, 2019 09:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save laacz/22ac4472e228c162495bd6ddf04e0f4f to your computer and use it in GitHub Desktop.
Save laacz/22ac4472e228c162495bd6ddf04e0f4f to your computer and use it in GitHub Desktop.
Remote (ESP8266) and physical switching of lights, state saving, etc.
/**
* This tiny ESP8266 sketch starts a web server,
* shows a button, allows to toggle one relay,
* saves its state to EEPROM, resotres upon reboot.
* Tries to connect to MQTT server to report on relay
* state changes via two topics.
*
* Sketch is used to play with simple multiway switches
* and ESP8266 + SPDT relay to be able to toggle lights
* from any of physical switches and also remotely.
*
* Also, it restores state upon restart, so there is
* are no accidentally switched on lights after electical
* outage in some cases.
*
* SPDT DPDT SPDT
* /+----+\ /+----+---+
* ---+/ X /+----[LAMP]
* +----+/ \+----+---+/
*
* Sketch is small, String's are here to stay.
*
* License: CC-BY
* Author: Kaspars Foigts
* Year: 2019
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>
#include <PubSubClient.h>
// WiFi access params
#define WIFI_SSID ""
#define WIFI_PASS ""
// MQTT config
#define MQTT_HOST ""
#define MQTT_NAME "I-Relay"
// Standard for Wemos relay shield
#define RELAY_PIN D1
// Default relay state
byte relay_state = HIGH;
ESP8266WebServer server(80);
WiFiClient espClient;
PubSubClient mqtt(espClient);
void connect_mqtt()
{
Serial.print(String("Connecting to MQTT server at ") + MQTT_HOST + " ");
mqtt.setServer(MQTT_HOST, 1883);
if (!mqtt.connected())
{
mqtt.connect(String(String(MQTT_NAME) + String(random(0xffff), HEX)).c_str());
delay(500);
Serial.print(".");
}
Serial.println(" Connected");
}
/**
* Sets relay according to relay_state, writes to EEPROM.
*/
void operateRelay()
{
Serial.println(String("Switching relay ") + (relay_state == HIGH ? "on" : "off"));
// Toggles relay itself
digitalWrite(RELAY_PIN, relay_state);
// Saves state
EEPROM.write(0, relay_state);
EEPROM.commit();
// Publish state to MQTT
String mqtt_topic;
String mqtt_payload;
if (mqtt.connected()) {
mqtt_topic = String("stat/") + MQTT_NAME + "/POWER";
mqtt_payload = relay_state ? "ON" : "OFF";
mqtt.publish(mqtt_topic.c_str(), mqtt_payload.c_str());
Serial.println(mqtt_topic + ": " + mqtt_payload);
mqtt_topic = String("stat/") + MQTT_NAME + "/RESULT";
mqtt_payload = String("{\"POWER\": \"") + (relay_state ? "ON" : "OFF") + "\"}";
mqtt.publish(mqtt_topic.c_str(), mqtt_payload.c_str());
Serial.println(mqtt_topic + ": " + mqtt_payload);
}
else
{
Serial.println("Skipping MQTT publishing, not (yet) connected.");
}
}
/**
* Retrns HTML header.
*/
String getHeader()
{
return String("<!DOCTYPE html><html><head>"
"<meta charset='utf-8'>"
"<link rel='stylesheet' href='//cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css'>"
"<meta name='viewport' content='width=device-width, initial-scale=1'>"
"</head><body>"
"<section class='section has-text-centered'><div class='container'>"
"<h1 class='title'>Relay toggler</h1>");
}
/**
* Retrns HTML footer.
*/
String getFooter()
{
return String("</div></section><footer class='footer'><p class='content has-text-centered'>") +
"Hello to you from " + server.client().remoteIP().toString() +
"<br>"
"Relay is on pin " +
RELAY_PIN +
"</p>"
"</footer><script>"
"document.addEventListener('DOMContentLoaded', () => {"
"document.getElementById('toggle').addEventListener('click', (evt) => {"
"evt.preventDefault();"
"fetch('/toggle', {"
"method: 'POST',"
"}).then((response) => response.json()).then((json) => {"
"document.getElementById('toggle').classList.toggle('is-primary', json.relay_state);"
"});"
"});"
"});"
"</script></body></html>";
}
/**
* Handles GET /
*/
void handleRoot()
{
// Logs to serial
Serial.println("/ from " + server.client().remoteIP().toString());
// Sends back HTML.
server.send(200, "text/html", getHeader() + "<p><button class='button " + (relay_state == 1 ? "is-primary" : "") + " is-large' id='toggle'>Poga</button></p>" + getFooter());
}
/**
* Handles POST /toggle
*/
void handleToggle()
{
// Changes relay state variable opposite to current one
relay_state = relay_state == HIGH ? LOW : HIGH;
operateRelay();
// Sends back state tp client as JSON
server.send(200, "application/json", String("{\"relay_state\": ") + relay_state + "}");
}
void setup()
{
Serial.begin(115200);
EEPROM.begin(sizeof(byte));
// This will be the pin which toggles relay state
pinMode(RELAY_PIN, OUTPUT);
// Restores state
relay_state = EEPROM.read(0);
// Let's switch relay on or off according to our plan
operateRelay();
// Connecting to designated WiFi
Serial.print(String("Connecting to ") + WIFI_SSID + " ");
WiFi.begin(WIFI_SSID, WIFI_PASS);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println(" connected.");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
// Add callbacks on various URIs
server.on("/", HTTP_GET, handleRoot);
server.on("/toggle", HTTP_POST, handleToggle);
server.begin();
Serial.println("HTTP server started");
}
void loop()
{
// Check if we have a request from anyone often
server.handleClient();
// If MQTT is not connected, connect
if (!mqtt.connected())
{
connect_mqtt();
}
mqtt.loop();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment