├── .gitattributes ├── CreateCalendarEvent.gs ├── README.md ├── ReadCalendarEvent.gs ├── ReminderV1 ├── HTTPSRedirect.cpp ├── HTTPSRedirect.h └── ReminderV1.ino ├── ReminderV2 ├── DebugMacros.h ├── HTTPSRedirect.cpp ├── HTTPSRedirect.h └── ReminderV2.ino ├── Reminder_Analog ├── DebugMacros.h ├── HTTPSRedirect.cpp ├── HTTPSRedirect.h └── Reminder_Analog.ino └── STL Files ├── 1_Butto_Lid.stl ├── 1_Button_Box.stl ├── 4_Button_Box.stl ├── 4_Button_Lid.stl ├── 8_Button_Box.stl └── 8_Button_Lid.stl /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /CreateCalendarEvent.gs: -------------------------------------------------------------------------------- 1 | function doGet(eve){ 2 | var title = eve.parameter.title; 3 | Logger.log(eve); 4 | //Logger.log(ContentService.createTextOutput(CreateEvent())); 5 | return ContentService.createTextOutput(CreateEvent(title)); 6 | } 7 | 8 | // Replace XXX with your calendar name 9 | function CreateEvent(eve){ 10 | var _calendarName = 'XXXs_Duties' 11 | var MILLIS_PER_HOUR = 1000 * 60 * 15 ; 12 | var now = new Date(); 13 | var from = new Date(now.getTime()-60000); 14 | var to = new Date(now.getTime() + 1 * MILLIS_PER_HOUR); 15 | var vCalendar = CalendarApp.getCalendarsByName(_calendarName)[0]; 16 | vCalendar.createEvent(eve, 17 | from, 18 | to) 19 | // Logger.log(currentTime); 20 | // Logger.log(vStartDateValue); 21 | // Logger.log(from); 22 | // Logger.log(to); 23 | // Logger.log(vEndDateValue); 24 | return true; 25 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Reminder with Google Calender 2 | 3 | V1 for compatibility to video https://youtu.be/sm1-l5-z3ag (probably no more working) 4 | 5 | Update Video: https://youtu.be/ZFgh2KFLeGA 6 | 7 | V2: For devices with up to 4 buttons. One pin for the switch and one pin for the LED 8 | 9 | Analog version: for up to 8 buttons on a Wemos D1 mini. Uses the analog pin A0 for the switches and one pin per LED 10 | -------------------------------------------------------------------------------- /ReadCalendarEvent.gs: -------------------------------------------------------------------------------- 1 | function doGet(){ 2 | // Logger.log(ContentService.createTextOutput(GetEvents())); 3 | return ContentService.createTextOutput(GetEvents()); 4 | } 5 | 6 | // Replace XXX with your calendar name 7 | 8 | function GetEvents(){ 9 | var _calendarName = 'XXXs_Duties' 10 | var Cal = CalendarApp.getCalendarsByName(_calendarName)[0]; 11 | var Now = new Date(); 12 | var Later = new Date(); 13 | Later.setSeconds(Now.getSeconds() + 1); 14 | // Logger.log(Now); 15 | Logger.log(Later); 16 | var events = Cal.getEvents(Now, Later); 17 | // Logger.log(events.length); 18 | str = ""; 19 | for (var i = 0; i < events.length; i++){ 20 | str += events[i].getTitle() + ';' ; 21 | //str += '\n'; 22 | } 23 | str = str.substring(0, str.length - 1); 24 | // Logger.log(str); 25 | return str; 26 | } -------------------------------------------------------------------------------- /ReminderV1/HTTPSRedirect.cpp: -------------------------------------------------------------------------------- 1 | /* HTTPS with follow-redirect 2 | * Created by Sujay S. Phadke, 2016 3 | * All rights reserved. 4 | * 5 | * Modified by Daniel Willi, 2016 6 | * 7 | */ 8 | 9 | #include "HTTPSRedirect.h" 10 | 11 | // from LarryD, Arduino forum 12 | #define DEBUG //If you comment this line, the DPRINT & DPRINTLN lines are defined as blank. 13 | #ifdef DEBUG //Macros are usually in all capital letters. 14 | #define DPRINT(...) Serial.print(__VA_ARGS__) //DPRINT is a macro, debug print 15 | #define DPRINTLN(...) Serial.println(__VA_ARGS__) //DPRINTLN is a macro, debug print with new line 16 | #else 17 | #define DPRINT(...) //now defines a blank line 18 | #define DPRINTLN(...) //now defines a blank line 19 | #endif 20 | 21 | String data; 22 | 23 | HTTPSRedirect::HTTPSRedirect(const int p, const char* fp, bool c) 24 | : httpsPort(p), redirFingerprint(fp), fpCheck(c){ 25 | } 26 | 27 | HTTPSRedirect::HTTPSRedirect(const int p) 28 | : httpsPort(p){ 29 | fpCheck = false; 30 | } 31 | 32 | HTTPSRedirect::~HTTPSRedirect(){ 33 | } 34 | 35 | String HTTPSRedirect::getData(String& url, const char* host, const char* redirHost){ 36 | return getData(url.c_str(), host, redirHost); 37 | } 38 | 39 | String HTTPSRedirect::getData(const char* url, const char* host, const char* redirHost){ 40 | 41 | int redirFlag = false; 42 | 43 | // Check if connection to host is alive 44 | if (!connected()){ 45 | Serial.println("Error! Not connected to host."); 46 | return "error"; 47 | } 48 | // HTTP headers must be terminated with a "\r\n\r\n" 49 | // http://stackoverflow.com/questions/6686261/what-at-the-bare-minimum-is-required-for-an-http-request 50 | // http://serverfault.com/questions/163511/what-is-the-mandatory-information-a-http-request-header-must-contain 51 | String Request = createRequest(url, host); 52 | 53 | DPRINTLN(Request); 54 | // make request 55 | print(Request); 56 | String line; 57 | String redirUrl; 58 | 59 | DPRINTLN("Detecting re-direction."); 60 | DPRINTLN(redirHost); 61 | 62 | while (connected()) { 63 | line = readStringUntil('\n'); 64 | DPRINTLN(line); 65 | if (line == "\r") { 66 | DPRINTLN("END OF HEADER"); 67 | 68 | //DPRINTLN(line); 69 | break; 70 | } 71 | 72 | if (find("Location: ") ){ 73 | find((char *)redirHost); 74 | DPRINTLN("Found re-direction URL!"); 75 | redirUrl = readStringUntil('\n'); 76 | redirFlag = true; 77 | break; 78 | } 79 | /* 80 | if (finder.findUntil("chunked", "\n\r") ){ 81 | break; 82 | }*/ 83 | } 84 | 85 | DPRINTLN("Body:\n"); 86 | if (verboseInfo){ 87 | fetchData(true, false); 88 | } 89 | 90 | else 91 | flush(); 92 | 93 | if (!redirFlag){ 94 | DPRINTLN("No re-direction URL found in header."); 95 | return "error"; 96 | } 97 | //flush(); 98 | //stop(); 99 | 100 | DPRINTLN("Redirected URL:"); 101 | DPRINTLN(redirUrl); 102 | 103 | Request = createRequest(redirUrl.c_str(), redirHost); 104 | 105 | DPRINTLN("Connecting to:"); 106 | DPRINTLN(redirHost); 107 | 108 | if (!connect(redirHost, httpsPort)) { 109 | Serial.println("Connection to re-directed host failed!"); 110 | return "error"; 111 | } 112 | 113 | if (fpCheck){ 114 | if (verify(redirFingerprint, redirHost)) { 115 | Serial.println("Re-directed host certificate match."); 116 | } else { 117 | Serial.println("Re-directed host certificate mis-match"); 118 | } 119 | } 120 | 121 | DPRINTLN("Requesting re-directed URL."); 122 | DPRINTLN(Request); 123 | 124 | // Make request 125 | print(Request); 126 | 127 | DPRINTLN("Final Response:"); 128 | 129 | fetchData(false, true); 130 | 131 | fetchData(true, false); 132 | 133 | flush(); 134 | //stop(); 135 | 136 | return data; 137 | } 138 | 139 | String HTTPSRedirect::createRequest(const char* url, const char* host){ 140 | return String("GET ") + url + " HTTP/1.1\r\n" + 141 | "Host: " + host + "\r\n" + 142 | "User-Agent: ESP8266\r\n" + 143 | (keepAlive ? "" : "Connection: close") + 144 | "\r\n\r\n"; 145 | 146 | } 147 | 148 | void HTTPSRedirect::fetchData(bool disp, bool header){ 149 | String line; 150 | data = ""; 151 | while (connected()) { 152 | line = readStringUntil('\n'); 153 | 154 | if (disp){ 155 | Serial.println(line); 156 | data += line + "|"; 157 | } 158 | if (line == "\r") { 159 | if (disp){ 160 | if (header){ 161 | DPRINTLN("END OF HEADER"); 162 | } 163 | else{ 164 | DPRINTLN("END OF RESPONSE"); 165 | DPRINTLN(line); 166 | } 167 | } 168 | data.remove(data.length()-3); 169 | //Serial.println("break"); 170 | break; 171 | } 172 | } 173 | } 174 | 175 | 176 | -------------------------------------------------------------------------------- /ReminderV1/HTTPSRedirect.h: -------------------------------------------------------------------------------- 1 | /* HTTPS with follow-redirect 2 | * Created by Sujay S. Phadke, 2016 3 | * All rights reserved. 4 | * 5 | * Modified by Daniel Willi, 2016 6 | * 7 | */ 8 | 9 | #include 10 | 11 | class HTTPSRedirect : public WiFiClientSecure { 12 | private: 13 | const int httpsPort; 14 | const char* redirFingerprint; 15 | bool fpCheck = false; 16 | bool keepAlive = true; 17 | bool verboseInfo = false; 18 | 19 | public: 20 | HTTPSRedirect(const int, const char*, bool); 21 | HTTPSRedirect(const int); 22 | ~HTTPSRedirect(); 23 | 24 | String getData(const char*, const char*, const char*); 25 | String getData(String&, const char*, const char*); 26 | String createRequest(const char*, const char*); 27 | void fetchData(bool, bool); 28 | 29 | }; 30 | 31 | -------------------------------------------------------------------------------- /ReminderV1/ReminderV1.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * 3 | Copyright <2018> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 6 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 13 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 14 | DEALINGS IN THE SOFTWARE. 15 | 16 | 17 | Based on: 18 | 19 | Google Calendar Integration ESP8266 20 | Created by Daniel Willi, 2016 21 | 22 | Based on the WifiClientSecure example by 23 | Ivan Grokhotkov 24 | 25 | */ 26 | 27 | // from LarryD, Arduino forum 28 | #define DEBUG //If you comment this line, the DPRINT & DPRINTLN lines are defined as blank. 29 | #ifdef DEBUG //Macros are usually in all capital letters. 30 | #define DPRINT(...) Serial.print(__VA_ARGS__) //DPRINT is a macro, debug print 31 | #define DPRINTLN(...) Serial.println(__VA_ARGS__) //DPRINTLN is a macro, debug print with new line 32 | #else 33 | #define DPRINT(...) //now defines a blank line 34 | #define DPRINTLN(...) //now defines a blank line 35 | #endif 36 | 37 | #include 38 | #include "HTTPSRedirect.h" 39 | //#include 40 | 41 | //Network credentials 42 | //Google Script ID 43 | const char *GScriptId = "AKacbdefg01ABC-ABcdeF55ff52sWW8KN"; //replace with you gscript id 44 | 45 | //Connection Settings 46 | const char* host = "script.google.com"; 47 | const char* googleRedirHost = "script.googleusercontent.com"; 48 | const int httpsPort = 443; 49 | 50 | unsigned long entryCalender, entryPrintStatus; 51 | 52 | //Fetch Google Calendar events 53 | String url = String("/macros/s/") + GScriptId + "/exec"; 54 | 55 | #define UPDATETIME 10000 56 | 57 | 58 | #ifdef CREDENTIALS 59 | const char* ssid = mySSID; 60 | const char* password = myPASSWORD; 61 | #else 62 | const char* ssid = ""; //replace with you ssid 63 | const char* password = ""; //replace with your password 64 | #endif 65 | 66 | 67 | #define NBR_EVENTS 4 68 | String possibleEvents[NBR_EVENTS] = {"Flower", "Paper", "Green", "Cardboard"}; 69 | byte LEDpins[NBR_EVENTS] = {D1, D2, D4, D6}; 70 | byte switchPins[NBR_EVENTS] = {D0, D3, D5, D7}; 71 | 72 | enum taskStatus { 73 | none, 74 | due, 75 | done 76 | }; 77 | 78 | taskStatus taskStatus[NBR_EVENTS]; 79 | 80 | String calendarData = ""; 81 | 82 | void setup() { 83 | Serial.begin(115200); 84 | 85 | connectToWifi(); 86 | for (int i = 0; i < NBR_EVENTS; i++) { 87 | pinMode(LEDpins[i], OUTPUT); 88 | taskStatus[i] = none; // Reset all LEDs 89 | pinMode(switchPins[i], INPUT_PULLUP); 90 | } 91 | } 92 | 93 | void loop() { 94 | if (millis() > entryCalender + UPDATETIME) { 95 | getCalendar(); 96 | entryCalender = millis(); 97 | } 98 | manageStatus(); 99 | setActivePins(); 100 | if (millis() > entryPrintStatus + 2000) { 101 | printStatus(); 102 | entryPrintStatus = millis(); 103 | } 104 | } 105 | 106 | //Turn active pins from array on 107 | void setActivePins() { 108 | for (int i = 0; i < NBR_EVENTS; i++) { 109 | if (taskStatus[i] == due) digitalWrite(LEDpins[i], HIGH); 110 | else digitalWrite(LEDpins[i], LOW); 111 | } 112 | } 113 | 114 | void printStatus() { 115 | for (int i = 0; i < NBR_EVENTS; i++) { 116 | Serial.print("Task "); 117 | Serial.print(i); 118 | Serial.print(" Status "); 119 | Serial.println(taskStatus[i]); 120 | } 121 | Serial.println("----------"); 122 | } 123 | 124 | //Connect to wifi 125 | void connectToWifi() { 126 | Serial.println(); 127 | Serial.print("Connecting to wifi: "); 128 | Serial.println(ssid); 129 | 130 | WiFi.begin(ssid, password); 131 | while (WiFi.status() != WL_CONNECTED) { 132 | delay(500); 133 | Serial.print("."); 134 | } 135 | 136 | Serial.println(""); 137 | Serial.println("WiFi connected"); 138 | Serial.println("IP address: "); 139 | Serial.println(WiFi.localIP()); 140 | 141 | // Use HTTPSRedirect class to create TLS connection 142 | HTTPSRedirect client(httpsPort); 143 | 144 | Serial.print("Connecting to "); 145 | Serial.println(host); 146 | bool flag = false; 147 | for (int i = 0; i < 5; i++) { 148 | int retval = client.connect(host, httpsPort); 149 | if (retval == 1) { 150 | flag = true; 151 | break; 152 | } 153 | else 154 | Serial.println("Connection failed. Retrying..."); 155 | } 156 | if (!flag) { 157 | Serial.print("Could not connect to server: "); 158 | Serial.println(host); 159 | Serial.println("Exiting..."); 160 | return; 161 | } 162 | } 163 | 164 | //Get calendar entries from google 165 | void getCalendar() { 166 | Serial.println("Start Request"); 167 | HTTPSRedirect client(httpsPort); 168 | if (!client.connected()) client.connect(host, httpsPort); 169 | calendarData = client.getData(url, host, googleRedirHost); 170 | Serial.print("Calender Data "); 171 | Serial.println(calendarData); 172 | } 173 | 174 | void manageStatus() { 175 | for (int i = 0; i < NBR_EVENTS; i++) { 176 | switch (taskStatus[i]) { 177 | case none: 178 | if (taskHere(i)) taskStatus[i] = due; 179 | break; 180 | case due: 181 | if (digitalRead(switchPins[i]) == false) taskStatus[i] = done; 182 | break; 183 | case done: 184 | if (taskHere(i) == false) taskStatus[i] = none; 185 | break; 186 | default: 187 | break; 188 | } 189 | } 190 | yield(); 191 | } 192 | 193 | bool taskHere(int task) { 194 | if (calendarData.indexOf(possibleEvents[task], 0) >= 0 ) { 195 | // Serial.print("Task found "); 196 | // Serial.println(task); 197 | return true; 198 | } else { 199 | // Serial.print("Task not found "); 200 | // Serial.println(task); 201 | return false; 202 | } 203 | } 204 | 205 | -------------------------------------------------------------------------------- /ReminderV2/DebugMacros.h: -------------------------------------------------------------------------------- 1 | // Variadic macros used to print information in de-bugging mode 2 | // from LarryD, Arduino forum 3 | 4 | #pragma once 5 | // un-comment this line to print the debugging statements 6 | //#define DEBUG 7 | 8 | #ifdef DEBUG 9 | #define DPRINT(...) Serial.print(__VA_ARGS__) 10 | #define DPRINTLN(...) Serial.println(__VA_ARGS__) 11 | #else 12 | // define blank line 13 | #define DPRINT(...) 14 | #define DPRINTLN(...) 15 | #endif 16 | -------------------------------------------------------------------------------- /ReminderV2/HTTPSRedirect.cpp: -------------------------------------------------------------------------------- 1 | /* HTTPS on ESP8266 with follow redirects, chunked encoding support 2 | * Version 2.1 3 | * Author: Sujay Phadke 4 | * Github: @electronicsguy 5 | * Copyright (C) 2017 Sujay Phadke 6 | * All rights reserved. 7 | * 8 | */ 9 | 10 | #include "HTTPSRedirect.h" 11 | #include "DebugMacros.h" 12 | 13 | HTTPSRedirect::HTTPSRedirect(void) : _httpsPort(443){ 14 | Init(); 15 | } 16 | 17 | HTTPSRedirect::HTTPSRedirect(const int p) : _httpsPort(p){ 18 | Init(); 19 | } 20 | 21 | HTTPSRedirect::~HTTPSRedirect(){ 22 | } 23 | 24 | void HTTPSRedirect::Init(void){ 25 | _keepAlive = true; 26 | _printResponseBody = false; 27 | _maxRedirects = 10; 28 | _contentTypeHeader = "application/x-www-form-urlencoded"; 29 | } 30 | 31 | // This is the main function which is similar to the method 32 | // print() from WifiClient or WifiClientSecure 33 | bool HTTPSRedirect::printRedir(void){ 34 | unsigned int httpStatus; 35 | 36 | // Check if connection to host is alive 37 | if (!connected()){ 38 | Serial.println("Error! Not connected to host."); 39 | return false; 40 | } 41 | 42 | // Clear the input stream of any junk data before making the request 43 | while(available()) 44 | read(); 45 | 46 | // Create HTTP/1.1 compliant request string 47 | // HTTP/1.1 complaint request packet must exist 48 | 49 | DPRINTLN(_Request); 50 | 51 | // Make the actual HTTPS request using the method 52 | // print() from the WifiClientSecure class 53 | // Make sure the input stream is cleared (as above) before making the call 54 | print(_Request); 55 | 56 | // Read HTTP Response Status lines 57 | while (connected()) { 58 | 59 | httpStatus = getResponseStatus(); 60 | 61 | // Only some HTTP response codes are checked for 62 | // http://www.restapitutorial.com/httpstatuscodes.html 63 | switch (httpStatus){ 64 | // Success. Fetch final response body 65 | case 200: 66 | case 201: 67 | { 68 | // final header is discarded 69 | fetchHeader(); 70 | 71 | #ifdef EXTRA_FNS 72 | printHeaderFields(); 73 | #endif 74 | 75 | if (_hF.transferEncoding == "chunked") 76 | fetchBodyChunked(); 77 | else 78 | fetchBodyUnChunked(_hF.contentLength); 79 | 80 | return true; 81 | } 82 | break; 83 | 84 | case 301: 85 | case 302: 86 | { 87 | // Get re-direction URL from the 'Location' field in the header 88 | if (getLocationURL()){ 89 | //stop(); // may not be required 90 | 91 | _myResponse.redirected = true; 92 | 93 | // Make a new connection to the re-direction server 94 | if (!connect(_redirHost.c_str(), _httpsPort)) { 95 | Serial.println("Connection to re-directed URL failed!"); 96 | return false; 97 | } 98 | 99 | // Recursive call to the requested URL on the server 100 | return printRedir(); 101 | 102 | } 103 | else{ 104 | Serial.println("Unable to retrieve redirection URL!"); 105 | return false; 106 | 107 | } 108 | } 109 | break; 110 | 111 | default: 112 | Serial.print("Error with request. Response status code: "); 113 | Serial.println(httpStatus); 114 | return false; 115 | break; 116 | } // end of switch 117 | 118 | } // end of while 119 | 120 | return false; 121 | 122 | } 123 | 124 | // Create a HTTP GET request packet 125 | // GET headers must be terminated with a "\r\n\r\n" 126 | // http://stackoverflow.com/questions/6686261/what-at-the-bare-minimum-is-required-for-an-http-request 127 | void HTTPSRedirect::createGetRequest(const String& url, const char* host){ 128 | _Request = String("GET ") + url + " HTTP/1.1\r\n" + 129 | "Host: " + host + "\r\n" + 130 | "User-Agent: ESP8266\r\n" + 131 | (_keepAlive ? "" : "Connection: close\r\n") + 132 | "\r\n\r\n"; 133 | 134 | return; 135 | } 136 | 137 | // Create a HTTP POST request packet 138 | // POST headers must be terminated with a "\r\n\r\n" 139 | // POST requests have 1 single blank like between the end of the header fields and the body payload 140 | void HTTPSRedirect::createPostRequest(const String& url, const char* host, const String& payload){ 141 | // Content-Length is mandatory in POST requests 142 | // Body content will include payload and a newline character 143 | unsigned int len = payload.length() + 1; 144 | 145 | _Request = String("POST ") + url + " HTTP/1.1\r\n" + 146 | "Host: " + host + "\r\n" + 147 | "User-Agent: ESP8266\r\n" + 148 | (_keepAlive ? "" : "Connection: close\r\n") + 149 | "Content-Type: " + _contentTypeHeader + "\r\n" + 150 | "Content-Length: " + len + "\r\n" + 151 | "\r\n" + 152 | payload + 153 | "\r\n\r\n"; 154 | 155 | return; 156 | } 157 | 158 | 159 | bool HTTPSRedirect::getLocationURL(void){ 160 | 161 | bool flag; 162 | 163 | // Keep reading from the input stream till we get to 164 | // the location field in the header 165 | flag = find("Location: "); 166 | 167 | if (flag){ 168 | // Skip URI protocol (http, https, etc. till '//') 169 | // This assumes that the location field will be containing 170 | // a URL of the form: http:/// 171 | readStringUntil('/'); 172 | readStringUntil('/'); 173 | // get hostname 174 | _redirHost = readStringUntil('/'); 175 | // get remaining url 176 | _redirUrl = String('/') + readStringUntil('\n'); 177 | } 178 | else{ 179 | DPRINT("No valid 'Location' field found in header!"); 180 | } 181 | 182 | // Create a GET request for the new location 183 | createGetRequest(_redirUrl, _redirHost.c_str()); 184 | 185 | DPRINT("_redirHost: "); 186 | DPRINTLN(_redirHost); 187 | DPRINT("_redirUrl: "); 188 | DPRINTLN(_redirUrl); 189 | 190 | return flag; 191 | } 192 | 193 | void HTTPSRedirect::fetchHeader(void){ 194 | String line = ""; 195 | int pos = -1; 196 | int pos2 = -1; 197 | int pos3 = -1; 198 | 199 | _hF.transferEncoding = ""; 200 | _hF.contentLength = 0; 201 | 202 | #ifdef EXTRA_FNS 203 | _hF.contentType = ""; 204 | #endif 205 | 206 | while (connected()) { 207 | line = readStringUntil('\n'); 208 | 209 | DPRINTLN(line); 210 | 211 | // HTTP headers are terminated by a CRLF ('\r\n') 212 | // Hence the final line will contain only '\r' 213 | // since we have already till the end ('\n') 214 | if (line == "\r") 215 | break; 216 | 217 | if (pos < 0){ 218 | pos = line.indexOf("Transfer-Encoding: "); 219 | if (!pos) 220 | // get string & remove trailing '\r' character to facilitate string comparisons 221 | _hF.transferEncoding = line.substring(19, line.length()-1); 222 | } 223 | if (pos2 < 0){ 224 | pos2 = line.indexOf("Content-Length: "); 225 | if (!pos2) 226 | _hF.contentLength = line.substring(16).toInt(); 227 | } 228 | #ifdef EXTRA_FNS 229 | if (pos3 < 0){ 230 | pos3 = line.indexOf("Content-Type: "); 231 | if (!pos3) 232 | // get string & remove trailing '\r' character to facilitate string comparisons 233 | _hF.contentType = line.substring(14, line.length()-1); 234 | } 235 | #endif 236 | 237 | } 238 | 239 | return; 240 | } 241 | 242 | void HTTPSRedirect::fetchBodyUnChunked(unsigned len){ 243 | String line; 244 | DPRINTLN("Body:"); 245 | 246 | while ((connected()) && (len > 0)) { 247 | line = readStringUntil('\n'); 248 | len -= line.length(); 249 | // Content length will include all '\n' terminating characters 250 | // Decrement once more to account for the '\n' line ending character 251 | --len; 252 | 253 | if (_printResponseBody) 254 | Serial.println(line); 255 | 256 | _myResponse.body += line; 257 | _myResponse.body += '\n'; 258 | 259 | } 260 | } 261 | 262 | // Ref: http://mihai.ibanescu.net/chunked-encoding-and-python-requests 263 | // http://fssnip.net/2t 264 | void HTTPSRedirect::fetchBodyChunked(void){ 265 | String line; 266 | int chunkSize; 267 | 268 | while (connected()){ 269 | line = readStringUntil('\n'); 270 | 271 | // Skip any empty lines 272 | if (line == "\r") 273 | continue; 274 | 275 | // Chunk sizes are in hexadecimal so convert to integer 276 | chunkSize = (uint32_t) strtol((const char *) line.c_str(), NULL, 16); 277 | DPRINT("Chunk Size: "); 278 | DPRINTLN(chunkSize); 279 | 280 | // Terminating chunk is of size 0 281 | if (chunkSize == 0) 282 | break; 283 | 284 | while (chunkSize > 0){ 285 | line = readStringUntil('\n'); 286 | if (_printResponseBody) 287 | Serial.println(line); 288 | 289 | _myResponse.body += line; 290 | _myResponse.body += '\n'; 291 | 292 | chunkSize -= line.length(); 293 | // The line above includes the '\r' character 294 | // which is not part of chunk size, so account for it 295 | --chunkSize; 296 | } 297 | 298 | // Skip over chunk trailer 299 | 300 | } 301 | 302 | return; 303 | 304 | } 305 | 306 | unsigned int HTTPSRedirect::getResponseStatus(void){ 307 | // Read response status line 308 | // ref: https://www.tutorialspoint.com/http/http_responses.htm 309 | 310 | unsigned int statusCode; 311 | String reasonPhrase; 312 | String line; 313 | 314 | unsigned int pos = -1; 315 | unsigned int pos2 = -1; 316 | 317 | // Skip any empty lines 318 | do{ 319 | line = readStringUntil('\n'); 320 | }while(line.length() == 0); 321 | 322 | pos = line.indexOf("HTTP/1.1 "); 323 | pos2 = line.indexOf(" ", 9); 324 | 325 | if (!pos){ 326 | statusCode = line.substring(9, pos2).toInt(); 327 | reasonPhrase = line.substring(pos2+1, line.length()-1); 328 | } 329 | else{ 330 | DPRINTLN("Error! No valid Status Code found in HTTP Response."); 331 | statusCode = 0; 332 | reasonPhrase = ""; 333 | } 334 | 335 | _myResponse.statusCode = statusCode; 336 | _myResponse.reasonPhrase = reasonPhrase; 337 | 338 | DPRINT("Status code: "); 339 | DPRINTLN(statusCode); 340 | DPRINT("Reason phrase: "); 341 | DPRINTLN(reasonPhrase); 342 | 343 | return statusCode; 344 | } 345 | 346 | bool HTTPSRedirect::GET(const String& url, const char* host){ 347 | return GET(url, host, _printResponseBody); 348 | } 349 | 350 | bool HTTPSRedirect::GET(const String& url, const char* host, const bool& disp){ 351 | bool retval; 352 | bool oldval; 353 | 354 | // set _printResponseBody temporarily to argument passed 355 | oldval = _printResponseBody; 356 | _printResponseBody = disp; 357 | 358 | // redirected Host and Url need to be initialized in case a 359 | // reConnectFinalEndpoint() request is made after an initial request 360 | // which did not have redirection 361 | _redirHost = host; 362 | _redirUrl = url; 363 | 364 | InitResponse(); 365 | 366 | // Create request packet 367 | createGetRequest(url, host); 368 | 369 | // Calll request handler 370 | retval = printRedir(); 371 | 372 | _printResponseBody = oldval; 373 | return retval; 374 | } 375 | 376 | bool HTTPSRedirect::POST(const String& url, const char* host, const String& payload){ 377 | return POST(url, host, payload, _printResponseBody); 378 | } 379 | 380 | bool HTTPSRedirect::POST(const String& url, const char* host, const String& payload, const bool& disp){ 381 | bool retval; 382 | bool oldval; 383 | 384 | // set _printResponseBody temporarily to argument passed 385 | oldval = _printResponseBody; 386 | _printResponseBody = disp; 387 | 388 | // redirected Host and Url need to be initialized in case a 389 | // reConnectFinalEndpoint() request is made after an initial request 390 | // which did not have redirection 391 | _redirHost = host; 392 | _redirUrl = url; 393 | 394 | InitResponse(); 395 | 396 | // Create request packet 397 | createPostRequest(url, host, payload); 398 | 399 | // Call request handler 400 | retval = printRedir(); 401 | 402 | _printResponseBody = oldval; 403 | return retval; 404 | } 405 | 406 | void HTTPSRedirect::InitResponse(void){ 407 | // Init response data 408 | _myResponse.body = ""; 409 | _myResponse.statusCode = 0; 410 | _myResponse.reasonPhrase = ""; 411 | _myResponse.redirected = false; 412 | } 413 | 414 | int HTTPSRedirect::getStatusCode(void){ 415 | return _myResponse.statusCode; 416 | } 417 | 418 | String HTTPSRedirect::getReasonPhrase(void){ 419 | return _myResponse.reasonPhrase; 420 | } 421 | 422 | String HTTPSRedirect::getResponseBody(void){ 423 | return _myResponse.body; 424 | } 425 | 426 | void HTTPSRedirect::setPrintResponseBody(bool disp){ 427 | _printResponseBody = disp; 428 | } 429 | 430 | void HTTPSRedirect::setMaxRedirects(const unsigned int n){ 431 | _maxRedirects = n; // to-do: use this in code above 432 | } 433 | 434 | void HTTPSRedirect::setContentTypeHeader(const char *type){ 435 | _contentTypeHeader = type; 436 | } 437 | 438 | #ifdef OPTIMIZE_SPEED 439 | bool HTTPSRedirect::reConnectFinalEndpoint(void){ 440 | // disconnect if connection already exists 441 | if (connected()) 442 | stop(); 443 | 444 | DPRINT("_redirHost: "); 445 | DPRINTLN(_redirHost); 446 | DPRINT("_redirUrl: "); 447 | DPRINTLN(_redirUrl); 448 | 449 | // Connect to stored final endpoint 450 | if (!connect(_redirHost.c_str(), _httpsPort)) { 451 | DPRINTLN("Connection to final URL failed!"); 452 | return false; 453 | } 454 | 455 | // Valid request packed must already be 456 | // present at this point in the member variable _Request 457 | // from the previous GET() or POST() request 458 | 459 | // Make call to final endpoint 460 | return printRedir(); 461 | } 462 | #endif 463 | 464 | #ifdef EXTRA_FNS 465 | void HTTPSRedirect::fetchBodyRaw(void){ 466 | String line; 467 | 468 | while (connected()){ 469 | line = readStringUntil('\n'); 470 | if (_printResponseBody) 471 | Serial.println(line); 472 | 473 | _myResponse.body += line; 474 | _myResponse.body += '\n'; 475 | } 476 | } 477 | 478 | void HTTPSRedirect::printHeaderFields(void){ 479 | DPRINT("Transfer Encoding: "); 480 | DPRINTLN(_hF.transferEncoding); 481 | DPRINT("Content Length: "); 482 | DPRINTLN(_hF.contentLength); 483 | DPRINT("Content Type: "); 484 | DPRINTLN(_hF.contentType); 485 | } 486 | #endif 487 | 488 | -------------------------------------------------------------------------------- /ReminderV2/HTTPSRedirect.h: -------------------------------------------------------------------------------- 1 | /* HTTPS on ESP8266 with follow redirects, chunked encoding support 2 | * Version 2.1 3 | * Author: Sujay Phadke 4 | * Github: @electronicsguy 5 | * Copyright (C) 2017 Sujay Phadke 6 | * All rights reserved. 7 | * 8 | */ 9 | #pragma once 10 | #include 11 | 12 | // Un-comment for extra functionality 13 | //#define EXTRA_FNS 14 | #define OPTIMIZE_SPEED 15 | 16 | class HTTPSRedirect : public WiFiClientSecure { 17 | private: 18 | const int _httpsPort; 19 | bool _keepAlive; 20 | String _redirUrl; 21 | String _redirHost; 22 | unsigned int _maxRedirects; // to-do 23 | const char* _contentTypeHeader; 24 | 25 | struct headerFields{ 26 | String transferEncoding; 27 | unsigned int contentLength; 28 | #ifdef EXTRA_FNS 29 | String contentType; 30 | #endif 31 | }; 32 | 33 | headerFields _hF; 34 | 35 | String _Request; 36 | 37 | struct Response{ 38 | int statusCode; 39 | String reasonPhrase; 40 | bool redirected; 41 | String body; 42 | }; 43 | 44 | Response _myResponse; 45 | bool _printResponseBody; 46 | 47 | void Init(void); 48 | bool printRedir(void); 49 | void fetchHeader(void); 50 | bool getLocationURL(void); 51 | void fetchBodyUnChunked(unsigned); 52 | void fetchBodyChunked(void); 53 | unsigned int getResponseStatus(void); 54 | void InitResponse(void); 55 | void createGetRequest(const String&, const char*); 56 | void createPostRequest(const String&, const char*, const String&); 57 | 58 | #ifdef EXTRA_FNS 59 | void fetchBodyRaw(void); 60 | void printHeaderFields(void); 61 | #endif 62 | 63 | public: 64 | 65 | HTTPSRedirect(void); 66 | HTTPSRedirect(const int); 67 | ~HTTPSRedirect(); 68 | 69 | bool GET(const String&, const char*); 70 | bool GET(const String&, const char*, const bool&); 71 | bool POST(const String&, const char*, const String&); 72 | bool POST(const String&, const char*, const String&, const bool&); 73 | 74 | int getStatusCode(void); 75 | String getReasonPhrase(void); 76 | String getResponseBody(void); 77 | 78 | void setPrintResponseBody(bool); 79 | void setMaxRedirects(const unsigned int); 80 | 81 | void setContentTypeHeader(const char *); 82 | #ifdef OPTIMIZE_SPEED 83 | bool reConnectFinalEndpoint(void); 84 | #endif 85 | 86 | }; 87 | -------------------------------------------------------------------------------- /ReminderV2/ReminderV2.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright <2018> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 6 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 13 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 14 | DEALINGS IN THE SOFTWARE. 15 | 16 | 17 | Based on the HTTPS library of Sujay Phadke ( https://github.com/electronicsguy/ESP8266/tree/master/HTTPSRedirect ) 18 | 19 | */ 20 | 21 | 22 | 23 | #include 24 | #include "HTTPSRedirect.h" 25 | // #include 26 | 27 | /* 28 | The credentials.h file at least has to contain: 29 | char mySSID[]="your SSID"; 30 | char myPASSWORD[]="your Password"; 31 | const char *GScriptIdRead = "............"; //replace with you gscript id for reading the calendar 32 | const char *GScriptIdWrite = "..........."; //replace with you gscript id for writing the calendar 33 | It has to be placed in the libraries folder 34 | If you do not want a credentials file. delete the line: #include 35 | */ 36 | 37 | 38 | //Connection Settings 39 | const char* host = "script.google.com"; 40 | const char* googleRedirHost = "script.googleusercontent.com"; 41 | const int httpsPort = 443; 42 | 43 | unsigned long entryCalender, entryPrintStatus, entryInterrupt, heartBeatEntry, heartBeatLedEntry; 44 | String url; 45 | 46 | 47 | #define UPDATETIME 10000 48 | 49 | #ifdef CREDENTIALS 50 | const char* ssid = mySSID; 51 | const char* password = myPASSWORD; 52 | const char *GScriptIdRead = GoogleScriptIdRead; 53 | const char *GScriptIdWrite = GoogleScriptIdWrite; 54 | #else 55 | //Network credentials 56 | const char* ssid = "............"; 57 | const char* password = "............"; //replace with your password 58 | //Google Script ID 59 | const char *GScriptIdRead = "............"; //replace with you gscript id for reading the calendar 60 | const char *GScriptIdWrite = "..........."; //replace with you gscript id for writing the calendar 61 | #endif 62 | 63 | #define NBR_EVENTS 4 64 | 65 | 66 | String possibleEvents[NBR_EVENTS] = {"Laundry", "Meal", "Telephone", "Shop"}; 67 | byte LEDpins[NBR_EVENTS] = {D2, D7, D4, D8}; // connect LEDs to these pins or change pin number here 68 | byte switchPins[NBR_EVENTS] = {D1, D3, D5, D6}; // connect switches to these pins or change pin number here 69 | bool switchPressed[NBR_EVENTS]; 70 | boolean beat = false; 71 | int beatLED = 0; 72 | 73 | enum taskStatus { 74 | none, 75 | due, 76 | done 77 | }; 78 | 79 | taskStatus taskStatus[NBR_EVENTS]; 80 | HTTPSRedirect* client = nullptr; 81 | 82 | String calendarData = ""; 83 | bool calenderUpToDate; 84 | 85 | //Connect to wifi 86 | void connectToWifi() { 87 | Serial.println(); 88 | Serial.print("Connecting to wifi: "); 89 | Serial.println(ssid); 90 | 91 | WiFi.begin(ssid, password); 92 | while (WiFi.status() != WL_CONNECTED) { 93 | delay(500); 94 | Serial.print("."); 95 | } 96 | Serial.println(""); 97 | Serial.print("WiFi connected "); 98 | Serial.print("IP address: "); 99 | Serial.println(WiFi.localIP()); 100 | 101 | // Use HTTPSRedirect class to create a new TLS connection 102 | client = new HTTPSRedirect(httpsPort); 103 | client->setInsecure(); 104 | client->setPrintResponseBody(true); 105 | client->setContentTypeHeader("application/json"); 106 | 107 | Serial.print("Connecting to "); 108 | Serial.println(host); 109 | 110 | // Try to connect for a maximum of 5 times 111 | bool flag = false; 112 | for (int i = 0; i < 5; i++) { 113 | int retval = client->connect(host, httpsPort); 114 | if (retval == 1) { 115 | flag = true; 116 | break; 117 | } 118 | else 119 | Serial.println("Connection failed. Retrying..."); 120 | } 121 | 122 | if (!flag) { 123 | Serial.print("Could not connect to server: "); 124 | Serial.println(host); 125 | Serial.println("Exiting..."); 126 | ESP.reset(); 127 | } 128 | Serial.println("Connected to Google"); 129 | } 130 | 131 | void printStatus() { 132 | for (int i = 0; i < NBR_EVENTS; i++) { 133 | Serial.print("Task "); 134 | Serial.print(i); 135 | Serial.print(" Status "); 136 | Serial.println(taskStatus[i]); 137 | } 138 | Serial.println("----------"); 139 | } 140 | 141 | void getCalendar() { 142 | // Serial.println("Start Request"); 143 | // HTTPSRedirect client(httpsPort); 144 | unsigned long getCalenderEntry = millis(); 145 | 146 | // Try to connect for a maximum of 5 times 147 | bool flag = false; 148 | for (int i = 0; i < 5; i++) { 149 | int retval = client->connect(host, httpsPort); 150 | if (retval == 1) { 151 | flag = true; 152 | break; 153 | } 154 | else 155 | Serial.println("Connection failed. Retrying..."); 156 | } 157 | if (!flag) { 158 | Serial.print("Could not connect to server: "); 159 | Serial.println(host); 160 | Serial.println("Exiting..."); 161 | ESP.reset(); 162 | } 163 | //Fetch Google Calendar events 164 | String url = String("/macros/s/") + GScriptIdRead + "/exec"; 165 | client->GET(url, host); 166 | calendarData = client->getResponseBody(); 167 | Serial.print("Calendar Data---> "); 168 | Serial.println(calendarData); 169 | calenderUpToDate = true; 170 | yield(); 171 | } 172 | 173 | void createEvent(String title) { 174 | // Serial.println("Start Write Request"); 175 | 176 | // Try to connect for a maximum of 5 times 177 | bool flag = false; 178 | for (int i = 0; i < 5; i++) { 179 | int retval = client->connect(host, httpsPort); 180 | if (retval == 1) { 181 | flag = true; 182 | break; 183 | } 184 | else 185 | Serial.println("Connection failed. Retrying..."); 186 | } 187 | if (!flag) { 188 | Serial.print("Could not connect to server: "); 189 | Serial.println(host); 190 | Serial.println("Exiting..."); 191 | ESP.reset(); 192 | } 193 | // Create event on Google Calendar 194 | String url = String("/macros/s/") + GScriptIdWrite + "/exec" + "?title=" + title; 195 | client->GET(url, host); 196 | // Serial.println(url); 197 | Serial.println("Write Event created "); 198 | calenderUpToDate = false; 199 | } 200 | 201 | void manageStatus() { 202 | for (int i = 0; i < NBR_EVENTS; i++) { 203 | switch (taskStatus[i]) { 204 | case none: 205 | if (switchPressed[i]) { 206 | digitalWrite(LEDpins[i], HIGH); 207 | while (!calenderUpToDate) getCalendar(); 208 | if (!eventHere(i)) createEvent(possibleEvents[i]); 209 | Serial.print(i); 210 | Serial.println(" 0 -->1"); 211 | //getCalendar(); 212 | taskStatus[i] = due; 213 | } else { 214 | if (eventHere(i)) { 215 | digitalWrite(LEDpins[i], HIGH); 216 | Serial.print(i); 217 | Serial.println(" 0 -->1"); 218 | taskStatus[i] = due; 219 | } 220 | } 221 | break; 222 | case due: 223 | if (switchPressed[i]) { 224 | digitalWrite(LEDpins[i], LOW); 225 | Serial.print(i); 226 | Serial.println(" 1 -->2"); 227 | taskStatus[i] = done; 228 | } 229 | break; 230 | case done: 231 | if (calenderUpToDate && !eventHere(i)) { 232 | digitalWrite(LEDpins[i], LOW); 233 | Serial.print(i); 234 | Serial.println(" 2 -->0"); 235 | taskStatus[i] = none; 236 | } 237 | break; 238 | default: 239 | break; 240 | } 241 | switchPressed[i] = false; 242 | } 243 | yield(); 244 | } 245 | 246 | bool eventHere(int task) { 247 | if (calendarData.indexOf(possibleEvents[task], 0) >= 0 ) { 248 | // Serial.print("Task found "); 249 | // Serial.println(task); 250 | return true; 251 | } else { 252 | // Serial.print("Task not found "); 253 | // Serial.println(task); 254 | return false; 255 | } 256 | } 257 | 258 | ICACHE_RAM_ATTR void handleInterrupt() { 259 | if (millis() > entryInterrupt + 100) { 260 | entryInterrupt = millis(); 261 | for (int i = 0; i < NBR_EVENTS; i++) { 262 | if (digitalRead(switchPins[i]) == LOW) { 263 | switchPressed[i] = true; 264 | } 265 | } 266 | } 267 | } 268 | 269 | void setup() { 270 | Serial.begin(115200); 271 | Serial.println("Reminder_V2"); 272 | for (int i = 0; i < NBR_EVENTS; i++) { 273 | pinMode(LEDpins[i], OUTPUT); 274 | taskStatus[i] = none; // Reset all LEDs 275 | pinMode(switchPins[i], INPUT_PULLUP); 276 | switchPressed[i] = false; 277 | attachInterrupt(digitalPinToInterrupt(switchPins[i]), handleInterrupt, FALLING); 278 | } 279 | connectToWifi(); 280 | getCalendar(); 281 | entryCalender = millis(); 282 | } 283 | 284 | 285 | void loop() { 286 | if (millis() > entryCalender + UPDATETIME) { 287 | getCalendar(); 288 | entryCalender = millis(); 289 | } 290 | manageStatus(); 291 | if (millis() > entryPrintStatus + 5000) { 292 | printStatus(); 293 | entryPrintStatus = millis(); 294 | } 295 | if (millis() > heartBeatEntry + 30000) { 296 | beat = true; 297 | heartBeatEntry = millis(); 298 | } 299 | heartBeat(); 300 | } 301 | 302 | void heartBeat() { 303 | if (beat) { 304 | if ( millis() > heartBeatLedEntry + 100) { 305 | heartBeatLedEntry = millis(); 306 | if (beatLED < NBR_EVENTS) { 307 | 308 | if (beatLED > 0) digitalWrite(LEDpins[beatLED - 1], LOW); 309 | digitalWrite(LEDpins[beatLED], HIGH); 310 | beatLED++; 311 | } 312 | else { 313 | for (int i = 0; i < NBR_EVENTS; i++) { 314 | if (taskStatus[i] == due) digitalWrite(LEDpins[i], HIGH); 315 | else digitalWrite(LEDpins[i], LOW); 316 | } 317 | beatLED = 0; 318 | beat = false; 319 | } 320 | } 321 | } 322 | } 323 | -------------------------------------------------------------------------------- /Reminder_Analog/DebugMacros.h: -------------------------------------------------------------------------------- 1 | // Variadic macros used to print information in de-bugging mode 2 | // from LarryD, Arduino forum 3 | 4 | #pragma once 5 | // un-comment this line to print the debugging statements 6 | //#define DEBUG 7 | 8 | #ifdef DEBUG 9 | #define DPRINT(...) Serial.print(__VA_ARGS__) 10 | #define DPRINTLN(...) Serial.println(__VA_ARGS__) 11 | #else 12 | // define blank line 13 | #define DPRINT(...) 14 | #define DPRINTLN(...) 15 | #endif 16 | -------------------------------------------------------------------------------- /Reminder_Analog/HTTPSRedirect.cpp: -------------------------------------------------------------------------------- 1 | /* HTTPS on ESP8266 with follow redirects, chunked encoding support 2 | * Version 2.1 3 | * Author: Sujay Phadke 4 | * Github: @electronicsguy 5 | * Copyright (C) 2017 Sujay Phadke 6 | * All rights reserved. 7 | * 8 | */ 9 | 10 | #include "HTTPSRedirect.h" 11 | #include "DebugMacros.h" 12 | 13 | HTTPSRedirect::HTTPSRedirect(void) : _httpsPort(443){ 14 | Init(); 15 | } 16 | 17 | HTTPSRedirect::HTTPSRedirect(const int p) : _httpsPort(p){ 18 | Init(); 19 | } 20 | 21 | HTTPSRedirect::~HTTPSRedirect(){ 22 | } 23 | 24 | void HTTPSRedirect::Init(void){ 25 | _keepAlive = true; 26 | _printResponseBody = false; 27 | _maxRedirects = 10; 28 | _contentTypeHeader = "application/x-www-form-urlencoded"; 29 | } 30 | 31 | // This is the main function which is similar to the method 32 | // print() from WifiClient or WifiClientSecure 33 | bool HTTPSRedirect::printRedir(void){ 34 | unsigned int httpStatus; 35 | 36 | // Check if connection to host is alive 37 | if (!connected()){ 38 | Serial.println("Error! Not connected to host."); 39 | return false; 40 | } 41 | 42 | // Clear the input stream of any junk data before making the request 43 | while(available()) 44 | read(); 45 | 46 | // Create HTTP/1.1 compliant request string 47 | // HTTP/1.1 complaint request packet must exist 48 | 49 | DPRINTLN(_Request); 50 | 51 | // Make the actual HTTPS request using the method 52 | // print() from the WifiClientSecure class 53 | // Make sure the input stream is cleared (as above) before making the call 54 | print(_Request); 55 | 56 | // Read HTTP Response Status lines 57 | while (connected()) { 58 | 59 | httpStatus = getResponseStatus(); 60 | 61 | // Only some HTTP response codes are checked for 62 | // http://www.restapitutorial.com/httpstatuscodes.html 63 | switch (httpStatus){ 64 | // Success. Fetch final response body 65 | case 200: 66 | case 201: 67 | { 68 | // final header is discarded 69 | fetchHeader(); 70 | 71 | #ifdef EXTRA_FNS 72 | printHeaderFields(); 73 | #endif 74 | 75 | if (_hF.transferEncoding == "chunked") 76 | fetchBodyChunked(); 77 | else 78 | fetchBodyUnChunked(_hF.contentLength); 79 | 80 | return true; 81 | } 82 | break; 83 | 84 | case 301: 85 | case 302: 86 | { 87 | // Get re-direction URL from the 'Location' field in the header 88 | if (getLocationURL()){ 89 | //stop(); // may not be required 90 | 91 | _myResponse.redirected = true; 92 | 93 | // Make a new connection to the re-direction server 94 | if (!connect(_redirHost.c_str(), _httpsPort)) { 95 | Serial.println("Connection to re-directed URL failed!"); 96 | return false; 97 | } 98 | 99 | // Recursive call to the requested URL on the server 100 | return printRedir(); 101 | 102 | } 103 | else{ 104 | Serial.println("Unable to retrieve redirection URL!"); 105 | return false; 106 | 107 | } 108 | } 109 | break; 110 | 111 | default: 112 | Serial.print("Error with request. Response status code: "); 113 | Serial.println(httpStatus); 114 | return false; 115 | break; 116 | } // end of switch 117 | 118 | } // end of while 119 | 120 | return false; 121 | 122 | } 123 | 124 | // Create a HTTP GET request packet 125 | // GET headers must be terminated with a "\r\n\r\n" 126 | // http://stackoverflow.com/questions/6686261/what-at-the-bare-minimum-is-required-for-an-http-request 127 | void HTTPSRedirect::createGetRequest(const String& url, const char* host){ 128 | _Request = String("GET ") + url + " HTTP/1.1\r\n" + 129 | "Host: " + host + "\r\n" + 130 | "User-Agent: ESP8266\r\n" + 131 | (_keepAlive ? "" : "Connection: close\r\n") + 132 | "\r\n\r\n"; 133 | 134 | return; 135 | } 136 | 137 | // Create a HTTP POST request packet 138 | // POST headers must be terminated with a "\r\n\r\n" 139 | // POST requests have 1 single blank like between the end of the header fields and the body payload 140 | void HTTPSRedirect::createPostRequest(const String& url, const char* host, const String& payload){ 141 | // Content-Length is mandatory in POST requests 142 | // Body content will include payload and a newline character 143 | unsigned int len = payload.length() + 1; 144 | 145 | _Request = String("POST ") + url + " HTTP/1.1\r\n" + 146 | "Host: " + host + "\r\n" + 147 | "User-Agent: ESP8266\r\n" + 148 | (_keepAlive ? "" : "Connection: close\r\n") + 149 | "Content-Type: " + _contentTypeHeader + "\r\n" + 150 | "Content-Length: " + len + "\r\n" + 151 | "\r\n" + 152 | payload + 153 | "\r\n\r\n"; 154 | 155 | return; 156 | } 157 | 158 | 159 | bool HTTPSRedirect::getLocationURL(void){ 160 | 161 | bool flag; 162 | 163 | // Keep reading from the input stream till we get to 164 | // the location field in the header 165 | flag = find("Location: "); 166 | 167 | if (flag){ 168 | // Skip URI protocol (http, https, etc. till '//') 169 | // This assumes that the location field will be containing 170 | // a URL of the form: http:/// 171 | readStringUntil('/'); 172 | readStringUntil('/'); 173 | // get hostname 174 | _redirHost = readStringUntil('/'); 175 | // get remaining url 176 | _redirUrl = String('/') + readStringUntil('\n'); 177 | } 178 | else{ 179 | DPRINT("No valid 'Location' field found in header!"); 180 | } 181 | 182 | // Create a GET request for the new location 183 | createGetRequest(_redirUrl, _redirHost.c_str()); 184 | 185 | DPRINT("_redirHost: "); 186 | DPRINTLN(_redirHost); 187 | DPRINT("_redirUrl: "); 188 | DPRINTLN(_redirUrl); 189 | 190 | return flag; 191 | } 192 | 193 | void HTTPSRedirect::fetchHeader(void){ 194 | String line = ""; 195 | int pos = -1; 196 | int pos2 = -1; 197 | int pos3 = -1; 198 | 199 | _hF.transferEncoding = ""; 200 | _hF.contentLength = 0; 201 | 202 | #ifdef EXTRA_FNS 203 | _hF.contentType = ""; 204 | #endif 205 | 206 | while (connected()) { 207 | line = readStringUntil('\n'); 208 | 209 | DPRINTLN(line); 210 | 211 | // HTTP headers are terminated by a CRLF ('\r\n') 212 | // Hence the final line will contain only '\r' 213 | // since we have already till the end ('\n') 214 | if (line == "\r") 215 | break; 216 | 217 | if (pos < 0){ 218 | pos = line.indexOf("Transfer-Encoding: "); 219 | if (!pos) 220 | // get string & remove trailing '\r' character to facilitate string comparisons 221 | _hF.transferEncoding = line.substring(19, line.length()-1); 222 | } 223 | if (pos2 < 0){ 224 | pos2 = line.indexOf("Content-Length: "); 225 | if (!pos2) 226 | _hF.contentLength = line.substring(16).toInt(); 227 | } 228 | #ifdef EXTRA_FNS 229 | if (pos3 < 0){ 230 | pos3 = line.indexOf("Content-Type: "); 231 | if (!pos3) 232 | // get string & remove trailing '\r' character to facilitate string comparisons 233 | _hF.contentType = line.substring(14, line.length()-1); 234 | } 235 | #endif 236 | 237 | } 238 | 239 | return; 240 | } 241 | 242 | void HTTPSRedirect::fetchBodyUnChunked(unsigned len){ 243 | String line; 244 | DPRINTLN("Body:"); 245 | 246 | while ((connected()) && (len > 0)) { 247 | line = readStringUntil('\n'); 248 | len -= line.length(); 249 | // Content length will include all '\n' terminating characters 250 | // Decrement once more to account for the '\n' line ending character 251 | --len; 252 | 253 | if (_printResponseBody) 254 | Serial.println(line); 255 | 256 | _myResponse.body += line; 257 | _myResponse.body += '\n'; 258 | 259 | } 260 | } 261 | 262 | // Ref: http://mihai.ibanescu.net/chunked-encoding-and-python-requests 263 | // http://fssnip.net/2t 264 | void HTTPSRedirect::fetchBodyChunked(void){ 265 | String line; 266 | int chunkSize; 267 | 268 | while (connected()){ 269 | line = readStringUntil('\n'); 270 | 271 | // Skip any empty lines 272 | if (line == "\r") 273 | continue; 274 | 275 | // Chunk sizes are in hexadecimal so convert to integer 276 | chunkSize = (uint32_t) strtol((const char *) line.c_str(), NULL, 16); 277 | DPRINT("Chunk Size: "); 278 | DPRINTLN(chunkSize); 279 | 280 | // Terminating chunk is of size 0 281 | if (chunkSize == 0) 282 | break; 283 | 284 | while (chunkSize > 0){ 285 | line = readStringUntil('\n'); 286 | if (_printResponseBody) 287 | Serial.println(line); 288 | 289 | _myResponse.body += line; 290 | _myResponse.body += '\n'; 291 | 292 | chunkSize -= line.length(); 293 | // The line above includes the '\r' character 294 | // which is not part of chunk size, so account for it 295 | --chunkSize; 296 | } 297 | 298 | // Skip over chunk trailer 299 | 300 | } 301 | 302 | return; 303 | 304 | } 305 | 306 | unsigned int HTTPSRedirect::getResponseStatus(void){ 307 | // Read response status line 308 | // ref: https://www.tutorialspoint.com/http/http_responses.htm 309 | 310 | unsigned int statusCode; 311 | String reasonPhrase; 312 | String line; 313 | 314 | unsigned int pos = -1; 315 | unsigned int pos2 = -1; 316 | 317 | // Skip any empty lines 318 | do{ 319 | line = readStringUntil('\n'); 320 | }while(line.length() == 0); 321 | 322 | pos = line.indexOf("HTTP/1.1 "); 323 | pos2 = line.indexOf(" ", 9); 324 | 325 | if (!pos){ 326 | statusCode = line.substring(9, pos2).toInt(); 327 | reasonPhrase = line.substring(pos2+1, line.length()-1); 328 | } 329 | else{ 330 | DPRINTLN("Error! No valid Status Code found in HTTP Response."); 331 | statusCode = 0; 332 | reasonPhrase = ""; 333 | } 334 | 335 | _myResponse.statusCode = statusCode; 336 | _myResponse.reasonPhrase = reasonPhrase; 337 | 338 | DPRINT("Status code: "); 339 | DPRINTLN(statusCode); 340 | DPRINT("Reason phrase: "); 341 | DPRINTLN(reasonPhrase); 342 | 343 | return statusCode; 344 | } 345 | 346 | bool HTTPSRedirect::GET(const String& url, const char* host){ 347 | return GET(url, host, _printResponseBody); 348 | } 349 | 350 | bool HTTPSRedirect::GET(const String& url, const char* host, const bool& disp){ 351 | bool retval; 352 | bool oldval; 353 | 354 | // set _printResponseBody temporarily to argument passed 355 | oldval = _printResponseBody; 356 | _printResponseBody = disp; 357 | 358 | // redirected Host and Url need to be initialized in case a 359 | // reConnectFinalEndpoint() request is made after an initial request 360 | // which did not have redirection 361 | _redirHost = host; 362 | _redirUrl = url; 363 | 364 | InitResponse(); 365 | 366 | // Create request packet 367 | createGetRequest(url, host); 368 | 369 | // Calll request handler 370 | retval = printRedir(); 371 | 372 | _printResponseBody = oldval; 373 | return retval; 374 | } 375 | 376 | bool HTTPSRedirect::POST(const String& url, const char* host, const String& payload){ 377 | return POST(url, host, payload, _printResponseBody); 378 | } 379 | 380 | bool HTTPSRedirect::POST(const String& url, const char* host, const String& payload, const bool& disp){ 381 | bool retval; 382 | bool oldval; 383 | 384 | // set _printResponseBody temporarily to argument passed 385 | oldval = _printResponseBody; 386 | _printResponseBody = disp; 387 | 388 | // redirected Host and Url need to be initialized in case a 389 | // reConnectFinalEndpoint() request is made after an initial request 390 | // which did not have redirection 391 | _redirHost = host; 392 | _redirUrl = url; 393 | 394 | InitResponse(); 395 | 396 | // Create request packet 397 | createPostRequest(url, host, payload); 398 | 399 | // Call request handler 400 | retval = printRedir(); 401 | 402 | _printResponseBody = oldval; 403 | return retval; 404 | } 405 | 406 | void HTTPSRedirect::InitResponse(void){ 407 | // Init response data 408 | _myResponse.body = ""; 409 | _myResponse.statusCode = 0; 410 | _myResponse.reasonPhrase = ""; 411 | _myResponse.redirected = false; 412 | } 413 | 414 | int HTTPSRedirect::getStatusCode(void){ 415 | return _myResponse.statusCode; 416 | } 417 | 418 | String HTTPSRedirect::getReasonPhrase(void){ 419 | return _myResponse.reasonPhrase; 420 | } 421 | 422 | String HTTPSRedirect::getResponseBody(void){ 423 | return _myResponse.body; 424 | } 425 | 426 | void HTTPSRedirect::setPrintResponseBody(bool disp){ 427 | _printResponseBody = disp; 428 | } 429 | 430 | void HTTPSRedirect::setMaxRedirects(const unsigned int n){ 431 | _maxRedirects = n; // to-do: use this in code above 432 | } 433 | 434 | void HTTPSRedirect::setContentTypeHeader(const char *type){ 435 | _contentTypeHeader = type; 436 | } 437 | 438 | #ifdef OPTIMIZE_SPEED 439 | bool HTTPSRedirect::reConnectFinalEndpoint(void){ 440 | // disconnect if connection already exists 441 | if (connected()) 442 | stop(); 443 | 444 | DPRINT("_redirHost: "); 445 | DPRINTLN(_redirHost); 446 | DPRINT("_redirUrl: "); 447 | DPRINTLN(_redirUrl); 448 | 449 | // Connect to stored final endpoint 450 | if (!connect(_redirHost.c_str(), _httpsPort)) { 451 | DPRINTLN("Connection to final URL failed!"); 452 | return false; 453 | } 454 | 455 | // Valid request packed must already be 456 | // present at this point in the member variable _Request 457 | // from the previous GET() or POST() request 458 | 459 | // Make call to final endpoint 460 | return printRedir(); 461 | } 462 | #endif 463 | 464 | #ifdef EXTRA_FNS 465 | void HTTPSRedirect::fetchBodyRaw(void){ 466 | String line; 467 | 468 | while (connected()){ 469 | line = readStringUntil('\n'); 470 | if (_printResponseBody) 471 | Serial.println(line); 472 | 473 | _myResponse.body += line; 474 | _myResponse.body += '\n'; 475 | } 476 | } 477 | 478 | void HTTPSRedirect::printHeaderFields(void){ 479 | DPRINT("Transfer Encoding: "); 480 | DPRINTLN(_hF.transferEncoding); 481 | DPRINT("Content Length: "); 482 | DPRINTLN(_hF.contentLength); 483 | DPRINT("Content Type: "); 484 | DPRINTLN(_hF.contentType); 485 | } 486 | #endif 487 | 488 | -------------------------------------------------------------------------------- /Reminder_Analog/HTTPSRedirect.h: -------------------------------------------------------------------------------- 1 | /* HTTPS on ESP8266 with follow redirects, chunked encoding support 2 | * Version 2.1 3 | * Author: Sujay Phadke 4 | * Github: @electronicsguy 5 | * Copyright (C) 2017 Sujay Phadke 6 | * All rights reserved. 7 | * 8 | */ 9 | #pragma once 10 | #include 11 | 12 | // Un-comment for extra functionality 13 | //#define EXTRA_FNS 14 | #define OPTIMIZE_SPEED 15 | 16 | class HTTPSRedirect : public WiFiClientSecure { 17 | private: 18 | const int _httpsPort; 19 | bool _keepAlive; 20 | String _redirUrl; 21 | String _redirHost; 22 | unsigned int _maxRedirects; // to-do 23 | const char* _contentTypeHeader; 24 | 25 | struct headerFields{ 26 | String transferEncoding; 27 | unsigned int contentLength; 28 | #ifdef EXTRA_FNS 29 | String contentType; 30 | #endif 31 | }; 32 | 33 | headerFields _hF; 34 | 35 | String _Request; 36 | 37 | struct Response{ 38 | int statusCode; 39 | String reasonPhrase; 40 | bool redirected; 41 | String body; 42 | }; 43 | 44 | Response _myResponse; 45 | bool _printResponseBody; 46 | 47 | void Init(void); 48 | bool printRedir(void); 49 | void fetchHeader(void); 50 | bool getLocationURL(void); 51 | void fetchBodyUnChunked(unsigned); 52 | void fetchBodyChunked(void); 53 | unsigned int getResponseStatus(void); 54 | void InitResponse(void); 55 | void createGetRequest(const String&, const char*); 56 | void createPostRequest(const String&, const char*, const String&); 57 | 58 | #ifdef EXTRA_FNS 59 | void fetchBodyRaw(void); 60 | void printHeaderFields(void); 61 | #endif 62 | 63 | public: 64 | 65 | HTTPSRedirect(void); 66 | HTTPSRedirect(const int); 67 | ~HTTPSRedirect(); 68 | 69 | bool GET(const String&, const char*); 70 | bool GET(const String&, const char*, const bool&); 71 | bool POST(const String&, const char*, const String&); 72 | bool POST(const String&, const char*, const String&, const bool&); 73 | 74 | int getStatusCode(void); 75 | String getReasonPhrase(void); 76 | String getResponseBody(void); 77 | 78 | void setPrintResponseBody(bool); 79 | void setMaxRedirects(const unsigned int); 80 | 81 | void setContentTypeHeader(const char *); 82 | #ifdef OPTIMIZE_SPEED 83 | bool reConnectFinalEndpoint(void); 84 | #endif 85 | 86 | }; 87 | -------------------------------------------------------------------------------- /Reminder_Analog/Reminder_Analog.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright <2018> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), 6 | to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 12 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 13 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 14 | DEALINGS IN THE SOFTWARE. 15 | 16 | 17 | Based on the HTTPS library of Sujay Phadke ( https://github.com/electronicsguy/ESP8266/tree/master/HTTPSRedirect ) 18 | 19 | */ 20 | 21 | #include 22 | #include 23 | #include "HTTPSRedirect.h" 24 | // #include 25 | 26 | /* 27 | The credentials.h file at least has to contain: 28 | char mySSID[]="your SSID"; 29 | char myPASSWORD[]="your Password"; 30 | const char *GScriptIdRead = "............"; //replace with you gscript id for reading the calendar 31 | const char *GScriptIdWrite = "..........."; //replace with you gscript id for writing the calendar 32 | It has to be placed in the libraries folder 33 | If you do not want a credentials file. delete the line: #include 34 | */ 35 | 36 | Ticker blinker; 37 | 38 | //Connection Settings 39 | const char* host = "script.google.com"; 40 | // const char* googleRedirHost = "script.googleusercontent.com"; 41 | const int httpsPort = 443; 42 | 43 | unsigned long entryCalender, entryPrintStatus, entrySwitchPressed, heartBeatEntry, heartBeatLedEntry; 44 | 45 | unsigned long intEntry; 46 | 47 | 48 | #define UPDATETIME 20000 49 | 50 | #ifdef CREDENTIALS 51 | const char* ssid = mySSID; 52 | const char* password = myPASSWORD; 53 | const char *GScriptIdRead = GoogleScriptIdRead; 54 | const char *GScriptIdWrite = GoogleScriptIdWrite; 55 | #else 56 | //Network credentials 57 | const char* ssid = "........."; //replace with you ssid 58 | const char* password = ".........."; //replace with your password 59 | //Google Script ID 60 | const char *GScriptIdRead = "............"; //replace with you gscript id for reading the calendar 61 | const char *GScriptIdWrite = "..........."; //replace with you gscript id for writing the calendar 62 | #endif 63 | 64 | #define NBR_EVENTS 8 65 | String possibleEvents[NBR_EVENTS] = {"Cat", "Paper", "Cardboard", "Green", "Laundry", "Fitness", "Meal", "XXX"}; 66 | //String possibleEvents[NBR_EVENTS] = {"Cat", "Paper", "Green", "Cardboard"}; 67 | byte LEDpins[NBR_EVENTS] = {D0, D1, D2, D4, D5, D6, D7, D8}; 68 | bool switchPressed[NBR_EVENTS]; 69 | unsigned long switchOff = 0; 70 | boolean beat = false; 71 | int beatLED = 0; 72 | 73 | // echo | openssl s_client -connect script.google.com:443 |& openssl x509 -fingerprint -noout 74 | const char* fingerprint = "96 38 33 60 D4 6B 84 C9 32 67 49 44 F2 27 D8 7C 33 1A 35 5A"; 75 | 76 | enum taskStatus { 77 | none, 78 | due, 79 | done 80 | }; 81 | 82 | taskStatus taskStatus[NBR_EVENTS]; 83 | HTTPSRedirect* client = nullptr; 84 | 85 | String calendarData = ""; 86 | bool calenderUpToDate; 87 | 88 | //Connect to wifi 89 | void connectToWifi() { 90 | Serial.println(); 91 | Serial.print("Connecting to wifi: "); 92 | Serial.println(ssid); 93 | 94 | WiFi.begin(ssid, password); 95 | while (WiFi.status() != WL_CONNECTED) { 96 | delay(500); 97 | Serial.print("."); 98 | } 99 | Serial.println(""); 100 | Serial.print("WiFi connected "); 101 | Serial.print("IP address: "); 102 | Serial.println(WiFi.localIP()); 103 | 104 | // Use HTTPSRedirect class to create a new TLS connection 105 | client = new HTTPSRedirect(httpsPort); 106 | client->setInsecure(); 107 | client->setPrintResponseBody(true); 108 | client->setContentTypeHeader("application/json"); 109 | 110 | Serial.print("Connecting to "); 111 | Serial.println(host); 112 | 113 | // Try to connect for a maximum of 5 times 114 | bool flag = false; 115 | for (int i = 0; i < 5; i++) { 116 | int retval = client->connect(host, httpsPort); 117 | if (retval == 1) { 118 | flag = true; 119 | break; 120 | } 121 | else 122 | Serial.println("Connection failed. Retrying..."); 123 | } 124 | 125 | if (!flag) { 126 | Serial.print("Could not connect to server: "); 127 | Serial.println(host); 128 | Serial.println("Exiting..."); 129 | ESP.reset(); 130 | } 131 | Serial.println("Connected to Google"); 132 | } 133 | 134 | void printStatus() { 135 | for (int i = 0; i < NBR_EVENTS; i++) { 136 | Serial.print("Task "); 137 | Serial.print(i); 138 | Serial.print(" Status "); 139 | Serial.println(taskStatus[i]); 140 | } 141 | Serial.println("----------"); 142 | } 143 | 144 | void getCalendar() { 145 | // Serial.println("Start Request"); 146 | // HTTPSRedirect client(httpsPort); 147 | unsigned long getCalenderEntry = millis(); 148 | 149 | // Try to connect for a maximum of 5 times 150 | bool flag = false; 151 | for (int i = 0; i < 5; i++) { 152 | int retval = client->connect(host, httpsPort); 153 | if (retval == 1) { 154 | flag = true; 155 | break; 156 | } 157 | else 158 | Serial.println("Connection failed. Retrying..."); 159 | } 160 | if (!flag) { 161 | Serial.print("Could not connect to server: "); 162 | Serial.println(host); 163 | Serial.println("Exiting..."); 164 | ESP.reset(); 165 | } 166 | //Fetch Google Calendar events 167 | String url = String("/macros/s/") + GScriptIdRead + "/exec"; 168 | client->GET(url, host); 169 | calendarData = client->getResponseBody(); 170 | Serial.print("Calendar Data---> "); 171 | Serial.println(calendarData); 172 | calenderUpToDate = true; 173 | yield(); 174 | } 175 | 176 | void createEvent(String title) { 177 | // Serial.println("Start Write Request"); 178 | 179 | // Try to connect for a maximum of 5 times 180 | bool flag = false; 181 | for (int i = 0; i < 5; i++) { 182 | int retval = client->connect(host, httpsPort); 183 | if (retval == 1) { 184 | flag = true; 185 | break; 186 | } 187 | else 188 | Serial.println("Connection failed. Retrying..."); 189 | } 190 | if (!flag) { 191 | Serial.print("Could not connect to server: "); 192 | Serial.println(host); 193 | Serial.println("Exiting..."); 194 | ESP.reset(); 195 | } 196 | // Create event on Google Calendar 197 | String url = String("/macros/s/") + GScriptIdWrite + "/exec" + "?title=" + title; 198 | client->GET(url, host); 199 | // Serial.println(url); 200 | Serial.println("Write Event created "); 201 | calenderUpToDate = false; 202 | } 203 | 204 | void manageStatus() { 205 | for (int i = 0; i < NBR_EVENTS; i++) { 206 | switch (taskStatus[i]) { 207 | case none: 208 | if (switchPressed[i]) { 209 | digitalWrite(LEDpins[i], HIGH); 210 | while (!calenderUpToDate) getCalendar(); 211 | if (!eventHere(i)) createEvent(possibleEvents[i]); 212 | Serial.print(i); 213 | Serial.println(" 0-->1"); 214 | //getCalendar(); 215 | taskStatus[i] = due; 216 | } else { 217 | if (eventHere(i)) { 218 | digitalWrite(LEDpins[i], HIGH); 219 | Serial.print(i); 220 | Serial.println(" 0-->1"); 221 | taskStatus[i] = due; 222 | } 223 | } 224 | break; 225 | case due: 226 | if (switchPressed[i]) { 227 | digitalWrite(LEDpins[i], LOW); 228 | Serial.print(i); 229 | Serial.println(" 1-->2"); 230 | taskStatus[i] = done; 231 | } 232 | break; 233 | case done: 234 | if (calenderUpToDate && !eventHere(i)) { 235 | digitalWrite(LEDpins[i], LOW); 236 | Serial.print(i); 237 | Serial.println(" 2-->0"); 238 | taskStatus[i] = none; 239 | } 240 | break; 241 | default: 242 | break; 243 | } 244 | switchPressed[i] = false; 245 | } 246 | yield(); 247 | } 248 | 249 | bool eventHere(int task) { 250 | if (calendarData.indexOf(possibleEvents[task], 0) >= 0 ) { 251 | // Serial.print("Task found "); 252 | // Serial.println(task); 253 | return true; 254 | } else { 255 | // Serial.print("Task not found "); 256 | // Serial.println(task); 257 | return false; 258 | } 259 | } 260 | 261 | void handleInterrupt() { 262 | intEntry = millis(); 263 | int reading = analogRead(A0); 264 | float hi = (reading / (1024.0 / NBR_EVENTS) - 0.5); 265 | 266 | if (hi >= 0) { 267 | if (switchOff > 10) { 268 | int button = (int)hi; 269 | switchPressed[button] = true; 270 | } 271 | switchOff = 0; 272 | } 273 | else switchOff++; // no switch pressed 274 | } 275 | 276 | void setup() { 277 | Serial.begin(115200); 278 | blinker.attach_ms(50, handleInterrupt); 279 | 280 | Serial.println("Reminder_Analog"); 281 | connectToWifi(); 282 | for (int i = 0; i < NBR_EVENTS; i++) { 283 | pinMode(LEDpins[i], OUTPUT); 284 | taskStatus[i] = none; // Reset all LEDs 285 | switchPressed[i] = false; 286 | } 287 | getCalendar(); 288 | entryCalender = millis(); 289 | pinMode(D0, OUTPUT); 290 | } 291 | 292 | 293 | void loop() { 294 | if (millis() > entryCalender + UPDATETIME) { 295 | getCalendar(); 296 | entryCalender = millis(); 297 | } 298 | manageStatus(); 299 | if (millis() > entryPrintStatus + 5000) { 300 | printStatus(); 301 | entryPrintStatus = millis(); 302 | } 303 | 304 | if (millis() > heartBeatEntry + 30000) { 305 | beat = true; 306 | heartBeatEntry = millis(); 307 | } 308 | heartBeat(); 309 | } 310 | 311 | void heartBeat() { 312 | if (beat) { 313 | if ( millis() > heartBeatLedEntry + 100) { 314 | heartBeatLedEntry = millis(); 315 | if (beatLED < NBR_EVENTS) { 316 | 317 | if (beatLED > 0) digitalWrite(LEDpins[beatLED - 1], LOW); 318 | digitalWrite(LEDpins[beatLED], HIGH); 319 | beatLED++; 320 | } 321 | else { 322 | for (int i = 0; i < NBR_EVENTS; i++) { 323 | if (taskStatus[i] == due) digitalWrite(LEDpins[i], HIGH); 324 | else digitalWrite(LEDpins[i], LOW); 325 | } 326 | beatLED = 0; 327 | beat = false; 328 | } 329 | } 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /STL Files/1_Butto_Lid.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SensorsIot/Reminder-with-Google-Calender/b2418122168947398a3939d4634865a92d532752/STL Files/1_Butto_Lid.stl -------------------------------------------------------------------------------- /STL Files/1_Button_Box.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SensorsIot/Reminder-with-Google-Calender/b2418122168947398a3939d4634865a92d532752/STL Files/1_Button_Box.stl -------------------------------------------------------------------------------- /STL Files/4_Button_Box.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SensorsIot/Reminder-with-Google-Calender/b2418122168947398a3939d4634865a92d532752/STL Files/4_Button_Box.stl -------------------------------------------------------------------------------- /STL Files/4_Button_Lid.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SensorsIot/Reminder-with-Google-Calender/b2418122168947398a3939d4634865a92d532752/STL Files/4_Button_Lid.stl -------------------------------------------------------------------------------- /STL Files/8_Button_Box.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SensorsIot/Reminder-with-Google-Calender/b2418122168947398a3939d4634865a92d532752/STL Files/8_Button_Box.stl -------------------------------------------------------------------------------- /STL Files/8_Button_Lid.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SensorsIot/Reminder-with-Google-Calender/b2418122168947398a3939d4634865a92d532752/STL Files/8_Button_Lid.stl --------------------------------------------------------------------------------