+
+#include <TimeLib.h>
+#include <Time.h>
+#include <ESP8266WiFi.h>
+#include <WiFiUdp.h>
+#include <DNSServer.h>
+#include <ESP8266WebServer.h>
+#include <WiFiManager.h>
+#include <ESP8266mDNS.h>
+#include <FS.h>
+
+// prototytpes
+String getDate(time_t when);
+String getTime(time_t when);
+
+// The MOSFET that switches the appliance
+#define MOSFET D1
+
+#define MANAGER_AP "RemotePower"
+#define CONFIG_PORT 80
+#define NTP_SERVER "1.uk.pool.ntp.org"
+
+#define DEVICE_ON HIGH
+#define DEVICE_OFF LOW
+#define DEFAULT_DELAY 10000 // 10 seconds
+
+
+#define www_username "admin"
+#define www_password "wibble"
+
+ESP8266WebServer server(CONFIG_PORT);
+
+unsigned long trigtime = 0;
+unsigned long trighold = 0;
+
+// track the status of our output pin
+bool mosfet_status = false;
+
+const unsigned int localPort = 2390;
+IPAddress ntpServerIP;
+
+const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
+
+byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
+
+WiFiUDP udp;
+
+unsigned long ntp_lastset = 0;
+unsigned long ntp_lasttry = 0;
+
+
+
+void output_set(bool on)
+{
+ digitalWrite(MOSFET, on?DEVICE_ON:DEVICE_OFF);
+ mosfet_status = on;
+}
+
+/* compose and send an NTP time request packet */
+void ntp_send()
+{
+ if (ntpServerIP == INADDR_NONE) {
+ if (WiFi.hostByName(NTP_SERVER, ntpServerIP) == 1) {
+ if (ntpServerIP == IPAddress(1,0,0,0)) {
+ Serial.println("DNS lookup failed for " NTP_SERVER " try again later.");
+ ntpServerIP = INADDR_NONE;
+ return;
+ }
+ Serial.print("Got NTP server " NTP_SERVER " address ");
+ Serial.println(ntpServerIP);
+ } else {
+ Serial.println("DNS lookup of " NTP_SERVER " failed.");
+ return;
+ }
+ }
+
+ ntp_lasttry = millis();
+ memset(packetBuffer, 0, NTP_PACKET_SIZE);
+ // Initialize values needed to form NTP request
+ // (see URL above for details on the packets)
+ packetBuffer[0] = 0b11100011; // LI, Version, Mode
+ packetBuffer[1] = 0; // Stratum, or type of clock
+ packetBuffer[2] = 6; // Polling Interval
+ packetBuffer[3] = 0xEC; // Peer Clock Precision
+ // 8 bytes of zero for Root Delay & Root Dispersion
+ packetBuffer[12] = 49;
+ packetBuffer[13] = 0x4E;
+ packetBuffer[14] = 49;
+ packetBuffer[15] = 52;
+
+ // all NTP fields have been given values, now
+ // you can send a packet requesting a timestamp:
+ udp.beginPacket(ntpServerIP, 123); //NTP requests are to port 123
+ udp.write(packetBuffer, NTP_PACKET_SIZE);
+ udp.endPacket();
+
+ Serial.println("Sending NTP request");
+}
+
+/* request a time update from NTP and parse the result */
+time_t ntp_fetch()
+{
+ while (udp.parsePacket() > 0); // discard old udp packets
+ ntp_send();
+
+ uint32_t beginWait = millis();
+
+ while (millis() - beginWait < 2500) {
+ int size = udp.parsePacket();
+ if (size >= NTP_PACKET_SIZE) {
+ udp.read(packetBuffer, NTP_PACKET_SIZE);
+
+ // this is NTP time (seconds since Jan 1 1900):
+ unsigned long secsSince1900 = packetBuffer[40] << 24 | packetBuffer[41] << 16 | packetBuffer[42] << 8 | packetBuffer[43];
+ const unsigned long seventyYears = 2208988800UL;
+ time_t unixtime = secsSince1900 - seventyYears;
+
+ ntp_lastset = millis();
+ Serial.print("NTP update unixtime=");
+ Serial.println(unixtime);
+ return unixtime;
+ }
+ }
+ Serial.println("No NTP response");
+ return 0;
+}
+
+
+String one_button_form(String target, String action, String blurb)
+{
+ String out = "";
+ out += "<form action=\"" + target + "\" method=\"POST\">";
+ out += "<input type=hidden name=action value=\"" + action + "\">";
+ out += "<input type=submit name=button value=\"" + blurb + "\">";
+ out += "</form>\n";
+
+ return out;
+}
+
+
+/* HTTP page request for / */
+void handleRoot()
+{
+ char mtime[16];
+ int sec = millis() / 1000;
+ int mi = sec / 60;
+ int hr = mi / 60;
+ int day = hr / 24;
+
+ snprintf(mtime, 16, "%dd %02d:%02d:%02d", day, hr % 24, mi % 60, sec % 60);
+
+ if (server.hasArg("action")) {
+ String action = server.arg("action");
+
+ if (action == "on") {
+ output_set(DEVICE_ON);
+ trigtime = 0;
+ } else
+ if (action == "off") {
+ output_set(DEVICE_OFF);
+ trigtime = 0;
+ } else
+ if (action == "toggle") {
+ output_set(!mosfet_status);
+ trigtime = 0;
+ } else
+ if (action == "pulse") {
+ if (server.hasArg("dur")) {
+ trighold = server.arg("dur").toInt();
+ } else {
+ trighold = 0;
+ }
+ if (trighold == 0) trighold = DEFAULT_DELAY;
+ trigtime = millis();
+ output_set(DEVICE_OFF);
+ }
+ }
+
+ String out = "<html>\
+ <head>\
+ <title>Remote Power</title>\
+ <style>\
+ body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; }\
+ </style>\
+ </head>\
+ <body>\
+ <h1>Remote Power</h1>\
+ <p>Uptime: " + (String)mtime + "</p>\n";
+
+ if (timeStatus() == timeSet) {
+ time_t when = now();
+ out += "<p>Time now: " + getDate(when) + " " + getTime(when) + "</p>\n";
+ }
+
+ FSInfo fs_info;
+ if (SPIFFS.info(fs_info)) {
+ out += "<p>Onboard Flash disk: - Size:"+String(fs_info.totalBytes)+" Used:"+String(fs_info.usedBytes)+"</p>\n";
+ }
+
+ out += "<p>Powered device is ";
+ out += mosfet_status?"ON":"OFF";
+ out += "(";
+ out += digitalRead(MOSFET) == HIGH?"HIGH":"LOW";
+ out += ")";
+
+ if (!mosfet_status && trigtime != 0) {
+ out += " and is due to turn back ON in ";
+ out += (trigtime + trighold - millis() )/ 1000;
+ out += " seconds.";
+ }
+
+ out += "</p>\n";
+
+ out += "<ul>\n";
+ out += "<li><a href=\"/reset\">Reset Configuration</a>\n";
+ out += "<li>" + one_button_form("/", "on", "Turn Device ON");
+ out += "<li>" + one_button_form("/", "off", "Turn Device OFF");
+ out += "<li>" + one_button_form("/", "toggle", "Toggle Device Status");
+ out += "<li>" + one_button_form("/", "pulse", (String)"Turn Device OFF for " + (int)(DEFAULT_DELAY/1000) + (String)" seconds");
+
+
+ out += "</ul>\n";
+ out += "</body>\n</html>\n";
+
+ server.send( 200, "text/html", out);
+}
+
+void handleNotFound() {
+ String out = "File Not found\n\n";
+ server.send(404, "text/plain", out);
+}
+
+// User wants to reset config
+void handleReset() {
+ if (!server.authenticate(www_username, www_password))
+ return server.requestAuthentication();
+
+ server.send(200, "text/plain", "Rebooting to config manager...\n\n");
+
+ WiFiManager wfm;
+ wfm.resetSettings();
+ WiFi.disconnect();
+ ESP.reset();
+ delay(5000);
+}
+
+void returnOK() {
+ server.send(200, "text/plain", "");
+}
+
+String getTime(time_t when)
+{
+ String ans;
+ int h = hour(when);
+ int m = minute(when);
+ int s = second(when);
+
+ if (h<10) ans += "0";
+ ans += String(h) + ":";
+ if (m<10) ans += "0";
+ ans += String(m) + ":";
+ if (s<10) ans += "0";
+ ans += String(s);
+
+ return ans;
+}
+
+String getDate(time_t when)
+{
+ String ans;
+
+ ans += String(year(when)) + "-" + String(month(when)) + "-" + String(day(when));
+ return ans;
+}
+
+
+void setup() {
+ // some serial, for debug
+ Serial.begin(115200);
+
+ // The lock mechanism
+ pinMode(MOSFET, OUTPUT);
+ output_set(DEVICE_ON);
+
+ Serial.println("Remote Power switcher");
+ Serial.println("Test WiFi and enter manager mode.");
+
+ WiFiManager wfm;
+ // Only wait in config mode for 3 minutes max
+ wfm.setConfigPortalTimeout(180);
+ // Try to connect to the old Ap for this long
+ wfm.setConnectTimeout(60);
+ // okay, lets try and connect...
+ wfm.autoConnect(MANAGER_AP);
+
+ Serial.println("Entering normal operation mode.");
+
+ // we have config and are running normally, setup web server
+ server.on( "/", handleRoot );
+ server.on( "/reset", handleReset );
+ server.onNotFound( handleNotFound );
+ server.begin();
+
+ // advertise we exist via MDNS
+ if (!MDNS.begin("remotepower")) {
+ Serial.println("Error setting up MDNS responder.");
+ } else {
+ MDNS.addService("http", "tcp", 80);
+ }
+
+ // enable internal flash
+ SPIFFS.begin();
+
+ Serial.println("Requesting time from network");
+ udp.begin(localPort);
+ setSyncProvider(ntp_fetch);
+
+ Serial.println("Remote power switch. Ready");
+}
+
+void loop() {
+ // put your main code here, to run repeatedly:
+ server.handleClient();
+
+ // a timed off state in progress
+ if (trigtime != 0) {
+ if (trigtime + trighold < millis()) {
+ trigtime = 0;
+ output_set(DEVICE_ON);
+ }
+ }
+
+ // has ntp failed. do we need to try again?
+ if (ntp_lastset == 0 && ntp_lasttry + 300000 < millis()) {
+ Serial.println("Ask Time service to try again");
+ setSyncProvider(ntp_fetch);
+ }
+
+}