Remote DC power control with a WeMos D1 mini
[remote_power] / remote_power.ino
CommitLineData
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
13String getDate(time_t when);
14String 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
31ESP8266WebServer server(CONFIG_PORT);
32
33unsigned long trigtime = 0;
34unsigned long trighold = 0;
35
36// track the status of our output pin
37bool mosfet_status = false;
38
39const unsigned int localPort = 2390;
40IPAddress ntpServerIP;
41
42const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
43
44byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
45
46WiFiUDP udp;
47
48unsigned long ntp_lastset = 0;
49unsigned long ntp_lasttry = 0;
50
51
52
53void 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 */
60void 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 */
101time_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
129String 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 / */
142void 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
228void handleNotFound() {
229 String out = "File Not found\n\n";
230 server.send(404, "text/plain", out);
231}
232
233// User wants to reset config
234void 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
247void returnOK() {
248 server.send(200, "text/plain", "");
249}
250
251String 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
268String 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
277void 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
321void 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}