-
Notifications
You must be signed in to change notification settings - Fork 1
/
SonoffServerAlexa.ino
452 lines (387 loc) · 12.7 KB
/
SonoffServerAlexa.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
/* By Th. Dillinger http://dillinger-engineering.de
* This template includes the following options
* Captiv Portal for logging on to a local WLAN router if the automatic connection fails
* OTA update
* Amazone Alexa suport and an integrated web browser
* Function parameter toggle for Webserverinterface integrated, it is nessesary for a Remotecontoll via http-Dashbutton
4MB/512SPIFFS flash sizee Memory replaced
Sonoff Programming interface
1 - vcc 3v3
2 - rx
3 - tx
4 - gnd
5 - gpio 14
esp8266 connections
gpio 0 - button
gpio 12 - relay
gpio 13 - green led - active low
gpio 14 - pin 5 on header
*/
#include "SonoffServerAlexa.h"
/* MDNSResponder is neded for
* For Mac OSX support is built in through Bonjour already.
For Linux, install Avahi.
For Windows, install Bonjour. */
//MDNSResponder mdns;
// Instanz for Alexa
Switch *sonoff = NULL;
UpnpBroadcastResponder upnpBroadcastResponder;
char *Hostname = "New SonOff";
static bool BLYNK_ENABLED = true;
int OnTime = 5;
unsigned TimeCounter;
const char RESPONSE_HEAD1[] PROGMEM = "<html><head><title>";
const char RESPONSE_HEAD2[] PROGMEM = "</title></head>";
const char RESPONSE_BODY[] PROGMEM = "<body><font color=\"#000000\"><body bgcolor=\"#d0d0f0\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=yes\">";
const char RESPONSE_TITEL1[] PROGMEM = "<h1><u>Sonoff Timerswitch - ";
const char RESPONSE_TITEL2[] PROGMEM = "</u></h1>";
const char RESPONSE_FORM[] PROGMEM = "<div><a href=\"on\"><button>ON</button></a> <a href=\"off\"><button>OFF</button></a> <a href=\"timer\"><button>Start Timer</button></a></div></p>";
const char RESPONSE_END[] PROGMEM = "<BR><BR><FONT SIZE=-1><hr/><a href=\"http://www.dillinger-engineering.de\" target=\"_blank\">www.dillinger-engineering.de</a><BR></body></html>";
typedef struct {
int salt = EEPROM_SALT;
char deviceName[33] = "New SonOff"; // default Modulname
char invocationname[33] = "Invocation Name"; // Alexa invocationname
} WMSettings;
WMSettings settings;
//needed for OTA Update
#ifdef OTA_UPDATE
#include <WiFiUdp.h>
#include <ArduinoOTA.h>
#endif
Ticker ticker; // For LED status
SimpleTimer timer; // Golbal Instans fo SimpleTimer called timer
const int CMD_WAIT = 0;
const int CMD_BUTTON_CHANGE = 1;
int cmd = CMD_WAIT;
int relayState = LOW;
//inverted button state
int buttonState = HIGH;
static long startPress = 0;
String GetwebPage(){
String webPage = String(RESPONSE_HEAD1) + String(Hostname) + String(RESPONSE_HEAD2) + String(RESPONSE_BODY) + String(RESPONSE_TITEL1)+ String(settings.invocationname) + String(RESPONSE_TITEL2) + String(RESPONSE_FORM);
// Auswahl der Wartezeit
webPage += "<form id=\"FSwitch\">";
webPage += "<label for=\"time\">Time:";
webPage += "<input name=\"time\" id=\"time\" type=\"number\" min=\"1\" max=\"1440\" step=\"1\" value=\"";
webPage += OnTime;
webPage += "\"> Min. ";
webPage += "<button type=\"submit\" form=\"FSwitch\" value=\"Submit\">Submit</button></form>";
webPage += "<BR><BR>Switch is: ";
if(relayState == HIGH){
webPage +=" ON";
}else{
webPage +=" OFF";
}
webPage += String(RESPONSE_END);
return(webPage);
}
void tick()
{
//toggle state
int state = digitalRead(SONOFF_LED); // get the current state of GPIO1 pin
digitalWrite(SONOFF_LED, !state); // set pin to the opposite state
}
//gets called when WiFiManager enters configuration mode
void configModeCallback (WiFiManager *myWiFiManager) {
#ifdef SER_DEBUG
Serial.println("Entered config mode");
Serial.println(WiFi.softAPIP());
//if you used auto generated SSID, print it
Serial.println(myWiFiManager->getConfigPortalSSID());
//entered config mode, make led toggle faster
#endif
ticker.attach(0.15, tick); // schenlles Blinken der LED
}
void setState(int s) {
digitalWrite(SONOFF_RELAY, s);
digitalWrite(SONOFF_LED, (s + 1) % 2); // led is active low (also invertiert, ist s=0 dann ist s+1=1 mod 2 = 0)
#ifdef SER_DEBUG
Serial.println(relayState);
#endif
}
void turnOn() {
#ifdef SER_DEBUG
Serial.print("relais state On: ");
#endif
ticker.detach();
TimeCounter = 0;
relayState = HIGH;
setState(relayState);
}
void turnOff() {
#ifdef SER_DEBUG
Serial.print("relais state Off: ");
#endif
ticker.detach();
TimeCounter = 0;
relayState = LOW;
setState(relayState);
}
void toggle() {
#ifdef SER_DEBUG
Serial.print("toggle state: ");
#endif
ticker.detach();
TimeCounter = 0;
relayState = relayState == HIGH ? LOW : HIGH;
setState(relayState);
}
void starttimer(){
turnOn(); // Relais ein
TimeCounter = OnTime * 60; // timer preset time
ticker.attach(2, tick); // slow blinking led (2 Sec.)
#ifdef SER_DEBUG
Serial.println("Timer stopp");
#endif
}
void onChangeInt(){
cmd = CMD_BUTTON_CHANGE;
}
void writeTimeToEEprom(int Time){
OnTime = Time;
EEPROM.begin(512);
EEPROM.put(500, Time);
EEPROM.end();
#ifdef SER_DEBUG
Serial.println(Time);
#endif
}
//flag for saving data
bool shouldSaveConfig = false;
//callback notifying us of the need to save config
void saveConfigCallback () {
#ifdef SER_DEBUG
Serial.println("Should save config");
#endif
shouldSaveConfig = true;
}
void reset() {
//reset settings to defaults
WMSettings defaults;
settings = defaults;
writeTimeToEEprom(5); // Default 5 minutes
//reset wifi credentials
WiFi.disconnect();
delay(1000);
ESP.reset();
delay(1000);
}
void restart() {
ESP.reset();
delay(1000);
}
/************************************* SETUP ****************************************************************/
void setup(){
#ifdef SER_DEBUG
Serial.begin(115200);
#endif
//set led pin as output
pinMode(SONOFF_LED, OUTPUT);
// start ticker with 0.6 because we start in AP mode and try to connect
ticker.attach(0.6, tick);
WiFiManager wifiManager;
//set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode
wifiManager.setAPCallback(configModeCallback);
//timeout - this will quit WiFiManager if it's not configured in 3 minutes, causing a restart
wifiManager.setConfigPortalTimeout(180);
//custom params
EEPROM.begin(512);
EEPROM.get(0, settings);
EEPROM.end();
if (settings.salt != EEPROM_SALT) {
#ifdef SER_DEBUG
Serial.println("Invalid settings in EEPROM, trying with defaults");
#endif
WMSettings defaults;
settings = defaults;
}
Hostname = settings.deviceName;
#ifdef SER_DEBUG
Serial.println(WiFi.localIP());
Serial.println("Alexa Invocation Name:");
Serial.println(settings.invocationname);
#endif
WiFiManagerParameter custom_devicename_text("Device name");
wifiManager.addParameter(&custom_devicename_text);
WiFiManagerParameter custom_devicename("device-name", "device name", settings.deviceName, 33);
wifiManager.addParameter(&custom_devicename);
WiFiManagerParameter custom_invocation_text("Alexa Invocation Name:");
wifiManager.addParameter(&custom_invocation_text);
WiFiManagerParameter custom_invocationname("invocation-name", "invocation name", settings.invocationname, 33);
wifiManager.addParameter(&custom_invocationname);
//set config save notify callback
wifiManager.setSaveConfigCallback(saveConfigCallback);
if (!wifiManager.autoConnect(Hostname)) {
#ifdef SER_DEBUG
Serial.println("failed to connect and hit timeout");
#endif
//reset and try again, or maybe put it to deep sleep
ESP.reset();
delay(1000);
}
//save the custom parameters to FS
if (shouldSaveConfig) {
strcpy(settings.deviceName, custom_devicename.getValue());
strcpy(settings.invocationname, custom_invocationname.getValue());
#ifdef SER_DEBUG
Serial.println("Saving config");
Serial.print("Device Name: ");
Serial.println(settings.deviceName);
Serial.print("Invocation Name: ");
Serial.println(settings.invocationname);
#endif
EEPROM.begin(512);
EEPROM.put(0, settings);
EEPROM.end();
}
#ifdef OTA_UPDATE
//OTA -Update
// Port defaults to 8266
// ArduinoOTA.setPort(8266);
// Hostname defaults to esp8266-[ChipID]
ArduinoOTA.setHostname(Hostname);
// No authentication by default
//ArduinoOTA.setPassword((const char *)"xc486u");
ArduinoOTA.onStart([]() {
#ifdef SER_DEBUG
Serial.println("Start");
#endif
});
ArduinoOTA.onEnd([]() {
#ifdef SER_DEBUG
Serial.println("\nEnd");
#endif
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
#ifdef SER_DEBUG
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
#endif
});
ArduinoOTA.onError([](ota_error_t error) {
#ifdef SER_DEBUG
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
else if (error == OTA_END_ERROR) Serial.println("End Failed");
#endif
});
ArduinoOTA.begin();
#ifdef SER_DEBUG
Serial.println("OTA started and ready");
#endif
#endif
//if you get here you have connected to the WiFi
#ifdef SER_DEBUG
Serial.println("connected... :)");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.print("Device Name: ");
Serial.println(settings.deviceName);
Serial.print("Invocation Name: ");
Serial.println(settings.invocationname);
#endif
/* if (mdns.begin(Hostname, WiFi.localIP())) {
#ifdef SER_DEBUG
Serial.println("MDNS responder started");
#endif
}*/
ticker.detach();
// if wifi connectet start upnp BroadcastResponder to connect with Alexa
upnpBroadcastResponder.beginUdpMulticast();
// Define your switches here. Max 14
// Format: Alexa invocation name, local port no, on callback, off callback
sonoff = new Switch(settings.invocationname, 80, turnOn, turnOff);
#ifdef SER_DEBUG
Serial.println("Adding switches upnp broadcast responder");
Serial.print("To invocation say: ");
Serial.println(settings.invocationname);
#endif
// add device for broadcast
upnpBroadcastResponder.addDevice(*sonoff);
// timed actions setup
timer.setInterval(1000, DecTime); // One timer is triggered every 1000 ms
//timer.setTimeout(10000, OnceOnlyTask); // Another timer is set to trigger only once after 10 seconds
//timer.setTimer(1200, TenTimesTask, 10); // Another timer is set to trigger 10 times
//setup button
pinMode(SONOFF_BUTTON, INPUT);
attachInterrupt(SONOFF_BUTTON, onChangeInt, CHANGE);
//setup relay
pinMode(SONOFF_RELAY, OUTPUT);
turnOff(); // Relais aus
//custom params
EEPROM.begin(512);
EEPROM.get(500, OnTime);
EEPROM.end();
if(OnTime<0){
OnTime=5;
}
#ifdef SER_DEBUG
Serial.print("Timer Zeit: ");
Serial.println(OnTime);
#endif
}
void DecTime(){
if(TimeCounter!=0){
TimeCounter --;
#ifdef SER_DEBUG
Serial.print("Timer Time: ");
Serial.println(TimeCounter);
#endif
if(TimeCounter==1){
ticker.detach();
turnOff(); // Relais aus
#ifdef SER_DEBUG
Serial.println("Timer stopp");
#endif
}
}
}
/************************************* LOOP ****************************************************************/
void loop(){
//ota loop
#ifdef OTA_UPDATE
ArduinoOTA.handle();
#endif
timer.run();
// handle upnp for Alexa
upnpBroadcastResponder.serverLoop();
sonoff->serverLoop();
switch (cmd) { // cmd = CMD_WAIT
case CMD_WAIT:
break;
case CMD_BUTTON_CHANGE:
int currentState = digitalRead(SONOFF_BUTTON);
if (currentState == LOW && currentState != buttonState){
startPress = millis();
buttonState = currentState;
}
if(currentState == HIGH){
cmd = CMD_WAIT;
buttonState = currentState;
long duration = millis() - startPress;
#ifdef SER_DEBUG
Serial.print("Duration: ");
Serial.println(duration);
#endif
if (duration > 8000) {
#ifdef SER_DEBUG
Serial.println("long press - reset settings");
#endif
reset();
}else if (duration > 4000) {
#ifdef SER_DEBUG
Serial.println("medium press - reset");
#endif
restart();
}else if (duration > 50) {
#ifdef SER_DEBUG
Serial.println("short press - toggle relay");
#endif
toggle();
}
}
break;
} // end Switch
}