Commit | Line | Data |
---|---|---|
c7f2bb87 B |
1 | |
2 | #include <TimeLib.h> | |
3 | #include <Time.h> | |
4 | #include <ESP8266WiFi.h> | |
5 | #include <WiFiUdp.h> | |
6 | #include <DNSServer.h> | |
7 | #include <ESP8266WebServer.h> | |
8 | #include <WiFiManager.h> | |
9 | #include <ESP8266mDNS.h> | |
10 | #include <FS.h> | |
11 | ||
12 | // prototytpes | |
13 | String getDate(time_t when); | |
14 | String getTime(time_t when); | |
15 | ||
16 | // The MOSFET that switches the appliance | |
17 | #define MOSFET D1 | |
18 | ||
19 | #define MANAGER_AP "RemotePower" | |
20 | #define CONFIG_PORT 80 | |
21 | #define NTP_SERVER "1.uk.pool.ntp.org" | |
22 | ||
23 | #define DEVICE_ON HIGH | |
24 | #define DEVICE_OFF LOW | |
25 | #define DEFAULT_DELAY 10000 // 10 seconds | |
26 | ||
27 | ||
28 | #define www_username "admin" | |
29 | #define www_password "wibble" | |
30 | ||
31 | ESP8266WebServer server(CONFIG_PORT); | |
32 | ||
33 | unsigned long trigtime = 0; | |
34 | unsigned long trighold = 0; | |
35 | ||
36 | // track the status of our output pin | |
37 | bool mosfet_status = false; | |
38 | ||
39 | const unsigned int localPort = 2390; | |
40 | IPAddress ntpServerIP; | |
41 | ||
42 | const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message | |
43 | ||
44 | byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets | |
45 | ||
46 | WiFiUDP udp; | |
47 | ||
48 | unsigned long ntp_lastset = 0; | |
49 | unsigned long ntp_lasttry = 0; | |
50 | ||
51 | ||
52 | ||
53 | void output_set(bool on) | |
54 | { | |
55 | digitalWrite(MOSFET, on?DEVICE_ON:DEVICE_OFF); | |
56 | mosfet_status = on; | |
57 | } | |
58 | ||
59 | /* compose and send an NTP time request packet */ | |
60 | void ntp_send() | |
61 | { | |
62 | if (ntpServerIP == INADDR_NONE) { | |
63 | if (WiFi.hostByName(NTP_SERVER, ntpServerIP) == 1) { | |
64 | if (ntpServerIP == IPAddress(1,0,0,0)) { | |
65 | Serial.println("DNS lookup failed for " NTP_SERVER " try again later."); | |
66 | ntpServerIP = INADDR_NONE; | |
67 | return; | |
68 | } | |
69 | Serial.print("Got NTP server " NTP_SERVER " address "); | |
70 | Serial.println(ntpServerIP); | |
71 | } else { | |
72 | Serial.println("DNS lookup of " NTP_SERVER " failed."); | |
73 | return; | |
74 | } | |
75 | } | |
76 | ||
77 | ntp_lasttry = millis(); | |
78 | memset(packetBuffer, 0, NTP_PACKET_SIZE); | |
79 | // Initialize values needed to form NTP request | |
80 | // (see URL above for details on the packets) | |
81 | packetBuffer[0] = 0b11100011; // LI, Version, Mode | |
82 | packetBuffer[1] = 0; // Stratum, or type of clock | |
83 | packetBuffer[2] = 6; // Polling Interval | |
84 | packetBuffer[3] = 0xEC; // Peer Clock Precision | |
85 | // 8 bytes of zero for Root Delay & Root Dispersion | |
86 | packetBuffer[12] = 49; | |
87 | packetBuffer[13] = 0x4E; | |
88 | packetBuffer[14] = 49; | |
89 | packetBuffer[15] = 52; | |
90 | ||
91 | // all NTP fields have been given values, now | |
92 | // you can send a packet requesting a timestamp: | |
93 | udp.beginPacket(ntpServerIP, 123); //NTP requests are to port 123 | |
94 | udp.write(packetBuffer, NTP_PACKET_SIZE); | |
95 | udp.endPacket(); | |
96 | ||
97 | Serial.println("Sending NTP request"); | |
98 | } | |
99 | ||
100 | /* request a time update from NTP and parse the result */ | |
101 | time_t ntp_fetch() | |
102 | { | |
103 | while (udp.parsePacket() > 0); // discard old udp packets | |
104 | ntp_send(); | |
105 | ||
106 | uint32_t beginWait = millis(); | |
107 | ||
108 | while (millis() - beginWait < 2500) { | |
109 | int size = udp.parsePacket(); | |
110 | if (size >= NTP_PACKET_SIZE) { | |
111 | udp.read(packetBuffer, NTP_PACKET_SIZE); | |
112 | ||
113 | // this is NTP time (seconds since Jan 1 1900): | |
114 | unsigned long secsSince1900 = packetBuffer[40] << 24 | packetBuffer[41] << 16 | packetBuffer[42] << 8 | packetBuffer[43]; | |
115 | const unsigned long seventyYears = 2208988800UL; | |
116 | time_t unixtime = secsSince1900 - seventyYears; | |
117 | ||
118 | ntp_lastset = millis(); | |
119 | Serial.print("NTP update unixtime="); | |
120 | Serial.println(unixtime); | |
121 | return unixtime; | |
122 | } | |
123 | } | |
124 | Serial.println("No NTP response"); | |
125 | return 0; | |
126 | } | |
127 | ||
128 | ||
129 | String one_button_form(String target, String action, String blurb) | |
130 | { | |
131 | String out = ""; | |
132 | out += "<form action=\"" + target + "\" method=\"POST\">"; | |
133 | out += "<input type=hidden name=action value=\"" + action + "\">"; | |
134 | out += "<input type=submit name=button value=\"" + blurb + "\">"; | |
135 | out += "</form>\n"; | |
136 | ||
137 | return out; | |
138 | } | |
139 | ||
140 | ||
141 | /* HTTP page request for / */ | |
142 | void handleRoot() | |
143 | { | |
144 | char mtime[16]; | |
145 | int sec = millis() / 1000; | |
146 | int mi = sec / 60; | |
147 | int hr = mi / 60; | |
148 | int day = hr / 24; | |
149 | ||
150 | snprintf(mtime, 16, "%dd %02d:%02d:%02d", day, hr % 24, mi % 60, sec % 60); | |
151 | ||
152 | if (server.hasArg("action")) { | |
153 | String action = server.arg("action"); | |
154 | ||
155 | if (action == "on") { | |
156 | output_set(DEVICE_ON); | |
157 | trigtime = 0; | |
158 | } else | |
159 | if (action == "off") { | |
160 | output_set(DEVICE_OFF); | |
161 | trigtime = 0; | |
162 | } else | |
163 | if (action == "toggle") { | |
164 | output_set(!mosfet_status); | |
165 | trigtime = 0; | |
166 | } else | |
167 | if (action == "pulse") { | |
168 | if (server.hasArg("dur")) { | |
169 | trighold = server.arg("dur").toInt(); | |
170 | } else { | |
171 | trighold = 0; | |
172 | } | |
173 | if (trighold == 0) trighold = DEFAULT_DELAY; | |
174 | trigtime = millis(); | |
175 | output_set(DEVICE_OFF); | |
176 | } | |
177 | } | |
178 | ||
179 | String out = "<html>\ | |
180 | <head>\ | |
181 | <title>Remote Power</title>\ | |
182 | <style>\ | |
183 | body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; }\ | |
184 | </style>\ | |
185 | </head>\ | |
186 | <body>\ | |
187 | <h1>Remote Power</h1>\ | |
188 | <p>Uptime: " + (String)mtime + "</p>\n"; | |
189 | ||
190 | if (timeStatus() == timeSet) { | |
191 | time_t when = now(); | |
192 | out += "<p>Time now: " + getDate(when) + " " + getTime(when) + "</p>\n"; | |
193 | } | |
194 | ||
195 | FSInfo fs_info; | |
196 | if (SPIFFS.info(fs_info)) { | |
197 | out += "<p>Onboard Flash disk: - Size:"+String(fs_info.totalBytes)+" Used:"+String(fs_info.usedBytes)+"</p>\n"; | |
198 | } | |
199 | ||
200 | out += "<p>Powered device is "; | |
201 | out += mosfet_status?"ON":"OFF"; | |
202 | out += "("; | |
203 | out += digitalRead(MOSFET) == HIGH?"HIGH":"LOW"; | |
204 | out += ")"; | |
205 | ||
206 | if (!mosfet_status && trigtime != 0) { | |
207 | out += " and is due to turn back ON in "; | |
208 | out += (trigtime + trighold - millis() )/ 1000; | |
209 | out += " seconds."; | |
210 | } | |
211 | ||
212 | out += "</p>\n"; | |
213 | ||
214 | out += "<ul>\n"; | |
215 | out += "<li><a href=\"/reset\">Reset Configuration</a>\n"; | |
216 | out += "<li>" + one_button_form("/", "on", "Turn Device ON"); | |
217 | out += "<li>" + one_button_form("/", "off", "Turn Device OFF"); | |
218 | out += "<li>" + one_button_form("/", "toggle", "Toggle Device Status"); | |
219 | out += "<li>" + one_button_form("/", "pulse", (String)"Turn Device OFF for " + (int)(DEFAULT_DELAY/1000) + (String)" seconds"); | |
220 | ||
221 | ||
222 | out += "</ul>\n"; | |
223 | out += "</body>\n</html>\n"; | |
224 | ||
225 | server.send( 200, "text/html", out); | |
226 | } | |
227 | ||
228 | void handleNotFound() { | |
229 | String out = "File Not found\n\n"; | |
230 | server.send(404, "text/plain", out); | |
231 | } | |
232 | ||
233 | // User wants to reset config | |
234 | void handleReset() { | |
235 | if (!server.authenticate(www_username, www_password)) | |
236 | return server.requestAuthentication(); | |
237 | ||
238 | server.send(200, "text/plain", "Rebooting to config manager...\n\n"); | |
239 | ||
240 | WiFiManager wfm; | |
241 | wfm.resetSettings(); | |
242 | WiFi.disconnect(); | |
243 | ESP.reset(); | |
244 | delay(5000); | |
245 | } | |
246 | ||
247 | void returnOK() { | |
248 | server.send(200, "text/plain", ""); | |
249 | } | |
250 | ||
251 | String getTime(time_t when) | |
252 | { | |
253 | String ans; | |
254 | int h = hour(when); | |
255 | int m = minute(when); | |
256 | int s = second(when); | |
257 | ||
258 | if (h<10) ans += "0"; | |
259 | ans += String(h) + ":"; | |
260 | if (m<10) ans += "0"; | |
261 | ans += String(m) + ":"; | |
262 | if (s<10) ans += "0"; | |
263 | ans += String(s); | |
264 | ||
265 | return ans; | |
266 | } | |
267 | ||
268 | String getDate(time_t when) | |
269 | { | |
270 | String ans; | |
271 | ||
272 | ans += String(year(when)) + "-" + String(month(when)) + "-" + String(day(when)); | |
273 | return ans; | |
274 | } | |
275 | ||
276 | ||
277 | void setup() { | |
278 | // some serial, for debug | |
279 | Serial.begin(115200); | |
280 | ||
281 | // The lock mechanism | |
282 | pinMode(MOSFET, OUTPUT); | |
283 | output_set(DEVICE_ON); | |
284 | ||
285 | Serial.println("Remote Power switcher"); | |
286 | Serial.println("Test WiFi and enter manager mode."); | |
287 | ||
288 | WiFiManager wfm; | |
289 | // Only wait in config mode for 3 minutes max | |
290 | wfm.setConfigPortalTimeout(180); | |
291 | // Try to connect to the old Ap for this long | |
292 | wfm.setConnectTimeout(60); | |
293 | // okay, lets try and connect... | |
294 | wfm.autoConnect(MANAGER_AP); | |
295 | ||
296 | Serial.println("Entering normal operation mode."); | |
297 | ||
298 | // we have config and are running normally, setup web server | |
299 | server.on( "/", handleRoot ); | |
300 | server.on( "/reset", handleReset ); | |
301 | server.onNotFound( handleNotFound ); | |
302 | server.begin(); | |
303 | ||
304 | // advertise we exist via MDNS | |
305 | if (!MDNS.begin("remotepower")) { | |
306 | Serial.println("Error setting up MDNS responder."); | |
307 | } else { | |
308 | MDNS.addService("http", "tcp", 80); | |
309 | } | |
310 | ||
311 | // enable internal flash | |
312 | SPIFFS.begin(); | |
313 | ||
314 | Serial.println("Requesting time from network"); | |
315 | udp.begin(localPort); | |
316 | setSyncProvider(ntp_fetch); | |
317 | ||
318 | Serial.println("Remote power switch. Ready"); | |
319 | } | |
320 | ||
321 | void loop() { | |
322 | // put your main code here, to run repeatedly: | |
323 | server.handleClient(); | |
324 | ||
325 | // a timed off state in progress | |
326 | if (trigtime != 0) { | |
327 | if (trigtime + trighold < millis()) { | |
328 | trigtime = 0; | |
329 | output_set(DEVICE_ON); | |
330 | } | |
331 | } | |
332 | ||
333 | // has ntp failed. do we need to try again? | |
334 | if (ntp_lastset == 0 && ntp_lasttry + 300000 < millis()) { | |
335 | Serial.println("Ask Time service to try again"); | |
336 | setSyncProvider(ntp_fetch); | |
337 | } | |
338 | ||
339 | } |