diff --git a/CalendarParser.cpp b/CalendarParser.cpp deleted file mode 100644 index e249eed..0000000 --- a/CalendarParser.cpp +++ /dev/null @@ -1,165 +0,0 @@ - -/**The MIT License (MIT) - -Copyright (c) 2017 by Daniel Eichhorn - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, 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: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -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 DEALINGS IN THE -SOFTWARE. - -See more at http://blog.squix.org -*/ - -#include -#include -#include "CalendarParser.h" - -CalendarParser::CalendarParser() { - -} - -void CalendarParser::updateCalendar(String GScriptId) { - - - const char* host = "script.google.com"; - String url = String("/macros/s/") + GScriptId + "/exec"; - JsonStreamingParser parser; - parser.setListener(this); - - /*const int httpPort = 80; - if (!client.connect("api.wunderground.com", httpPort)) { - Serial.println("connection failed"); - return; - } - - Serial.print("Requesting URL: "); - Serial.println(url); - - // This will send the request to the server - client.print(String("GET ") + url + " HTTP/1.1\r\n" + - "Host: api.wunderground.com\r\n" + - "Connection: close\r\n\r\n"); - int retryCounter = 0; - while(!client.available()) { - delay(1000); - retryCounter++; - if (retryCounter > 10) { - return; - } - }*/ - // Use HTTPSRedirect class to create a new TLS connection - int httpsPort = 443; - HTTPSRedirect *client = new HTTPSRedirect(); - client->setPrintResponseBody(true); - client->setContentTypeHeader("application/json"); - - Serial.print("Connecting to "); - Serial.println(url); - - // Try to connect for a maximum of 5 times - bool flag = false; - for (int i=0; i<5; i++){ - yield(); - Serial.print("X"); - int retval = client->connect("blog.squix.org", httpsPort); - Serial.print("x"); - if (retval == 1) { - flag = true; - break; - } - else - Serial.println("Connection failed. Retrying..."); - - } - - if (!flag){ - Serial.print("Could not connect to server: "); - Serial.println(host); - Serial.println("Exiting..."); - return; - } - - /*if (client->verify(fingerprint, host)) { - Serial.println("Certificate match."); - } else { - Serial.println("Certificate mis-match"); - }*/ - - - // fetch spreadsheet data - client->GET(url, host); - - - - - int pos = 0; - boolean isBody = false; - char c; - - int size = 0; - client->setNoDelay(false); - while(client->connected()) { - while((size = client->available()) > 0) { - c = client->read(); - if (c == '{' || c == '[') { - isBody = true; - } - if (isBody) { - parser.parse(c); - } - } - } -} - -void CalendarParser::whitespace(char c) { - Serial.println("whitespace"); -} - -void CalendarParser::startDocument() { - Serial.println("start document"); -} - -void CalendarParser::key(String key) { - currentKey = String(key); - -} - -void CalendarParser::value(String value) { - -} - -void CalendarParser::endArray() { - -} - - -void CalendarParser::startObject() { -} - -void CalendarParser::endObject() { - -} - -void CalendarParser::endDocument() { - -} - -void CalendarParser::startArray() { - -} - - diff --git a/CalendarParser.h b/CalendarParser.h deleted file mode 100644 index 079de97..0000000 --- a/CalendarParser.h +++ /dev/null @@ -1,57 +0,0 @@ -/**The MIT License (MIT) - -Copyright (c) 2017 by Daniel Eichhorn - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, 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: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -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 DEALINGS IN THE -SOFTWARE. - -See more at http://blog.squix.org -*/ - -#pragma once - -#include -#include -#include "HTTPSRedirect.h" - -class CalendarParser: public JsonListener { - private: - String currentKey; - - public: - CalendarParser(); - void updateCalendar(String GScriptId); - - virtual void whitespace(char c); - - virtual void startDocument(); - - virtual void key(String key); - - virtual void value(String value); - - virtual void endArray(); - - virtual void endObject(); - - virtual void endDocument(); - - virtual void startArray(); - - virtual void startObject(); -}; diff --git a/HttpsRedirect.cpp b/HttpsRedirect.cpp deleted file mode 100644 index a3ca3a4..0000000 --- a/HttpsRedirect.cpp +++ /dev/null @@ -1,486 +0,0 @@ -/* HTTPS on ESP8266 with follow redirects, chunked encoding support - * Version 2.1 - * Author: Sujay Phadke - * Github: @electronicsguy - * Copyright (C) 2017 Sujay Phadke - * All rights reserved. - * - */ - -#include "HTTPSRedirect.h" - -HTTPSRedirect::HTTPSRedirect(void) : _httpsPort(443){ - Init(); -} - -HTTPSRedirect::HTTPSRedirect(const int p) : _httpsPort(p){ - Init(); -} - -HTTPSRedirect::~HTTPSRedirect(){ -} - -void HTTPSRedirect::Init(void){ - _keepAlive = true; - _printResponseBody = false; - _maxRedirects = 10; - _contentTypeHeader = "application/x-www-form-urlencoded"; -} - -// This is the main function which is similar to the method -// print() from WifiClient or WifiClientSecure -bool HTTPSRedirect::printRedir(void){ - unsigned int httpStatus; - - // Check if connection to host is alive - if (!connected()){ - Serial.println("Error! Not connected to host."); - return false; - } - - // Clear the input stream of any junk data before making the request - while(available()) - read(); - - // Create HTTP/1.1 compliant request string - // HTTP/1.1 complaint request packet must exist - - DPRINTLN(_Request); - - // Make the actual HTTPS request using the method - // print() from the WifiClientSecure class - // Make sure the input stream is cleared (as above) before making the call - print(_Request); - - // Read HTTP Response Status lines - while (connected()) { - - httpStatus = getResponseStatus(); - - // Only some HTTP response codes are checked for - // http://www.restapitutorial.com/httpstatuscodes.html - switch (httpStatus){ - // Success. Fetch final response body - case 200: - case 201: - { - // final header is discarded - fetchHeader(); - - #ifdef EXTRA_FNS - printHeaderFields(); - #endif - - if (_hF.transferEncoding == "chunked") - fetchBodyChunked(); - else - fetchBodyUnChunked(_hF.contentLength); - - return true; - } - break; - - case 301: - case 302: - { - // Get re-direction URL from the 'Location' field in the header - if (getLocationURL()){ - //stop(); // may not be required - - _myResponse.redirected = true; - - // Make a new connection to the re-direction server - if (!connect(_redirHost.c_str(), _httpsPort)) { - Serial.println("Connection to re-directed URL failed!"); - return false; - } - - // Recursive call to the requested URL on the server - return printRedir(); - - } - else{ - Serial.println("Unable to retrieve redirection URL!"); - return false; - - } - } - break; - - default: - Serial.print("Error with request. Response status code: "); - Serial.println(httpStatus); - return false; - break; - } // end of switch - - } // end of while - - return false; - -} - -// Create a HTTP GET request packet -// GET headers must be terminated with a "\r\n\r\n" -// http://stackoverflow.com/questions/6686261/what-at-the-bare-minimum-is-required-for-an-http-request -void HTTPSRedirect::createGetRequest(const String& url, const char* host){ - _Request = String("GET ") + url + " HTTP/1.1\r\n" + - "Host: " + host + "\r\n" + - "User-Agent: ESP8266\r\n" + - (_keepAlive ? "" : "Connection: close\r\n") + - "\r\n\r\n"; - - return; -} - -// Create a HTTP POST request packet -// POST headers must be terminated with a "\r\n\r\n" -// POST requests have 1 single blank like between the end of the header fields and the body payload -void HTTPSRedirect::createPostRequest(const String& url, const char* host, const String& payload){ - // Content-Length is mandatory in POST requests - // Body content will include payload and a newline character - unsigned int len = payload.length() + 1; - - _Request = String("POST ") + url + " HTTP/1.1\r\n" + - "Host: " + host + "\r\n" + - "User-Agent: ESP8266\r\n" + - (_keepAlive ? "" : "Connection: close\r\n") + - "Content-Type: " + _contentTypeHeader + "\r\n" + - "Content-Length: " + len + "\r\n" + - "\r\n" + - payload + - "\r\n\r\n"; - - return; -} - - -bool HTTPSRedirect::getLocationURL(void){ - - bool flag; - - // Keep reading from the input stream till we get to - // the location field in the header - flag = find("Location: "); - - if (flag){ - // Skip URI protocol (http, https, etc. till '//') - // This assumes that the location field will be containing - // a URL of the form: http:/// - readStringUntil('/'); - readStringUntil('/'); - // get hostname - _redirHost = readStringUntil('/'); - // get remaining url - _redirUrl = String('/') + readStringUntil('\n'); - } - else{ - DPRINT("No valid 'Location' field found in header!"); - } - - // Create a GET request for the new location - createGetRequest(_redirUrl, _redirHost.c_str()); - - DPRINT("_redirHost: "); - DPRINTLN(_redirHost); - DPRINT("_redirUrl: "); - DPRINTLN(_redirUrl); - - return flag; -} - -void HTTPSRedirect::fetchHeader(void){ - String line = ""; - int pos = -1; - int pos2 = -1; - int pos3 = -1; - - _hF.transferEncoding = ""; - _hF.contentLength = 0; - - #ifdef EXTRA_FNS - _hF.contentType = ""; - #endif - - while (connected()) { - line = readStringUntil('\n'); - - DPRINTLN(line); - - // HTTP headers are terminated by a CRLF ('\r\n') - // Hence the final line will contain only '\r' - // since we have already till the end ('\n') - if (line == "\r") - break; - - if (pos < 0){ - pos = line.indexOf("Transfer-Encoding: "); - if (!pos) - // get string & remove trailing '\r' character to facilitate string comparisons - _hF.transferEncoding = line.substring(19, line.length()-1); - } - if (pos2 < 0){ - pos2 = line.indexOf("Content-Length: "); - if (!pos2) - _hF.contentLength = line.substring(16).toInt(); - } - #ifdef EXTRA_FNS - if (pos3 < 0){ - pos3 = line.indexOf("Content-Type: "); - if (!pos3) - // get string & remove trailing '\r' character to facilitate string comparisons - _hF.contentType = line.substring(14, line.length()-1); - } - #endif - - } - - return; -} - -void HTTPSRedirect::fetchBodyUnChunked(unsigned len){ - String line; - DPRINTLN("Body:"); - - while ((connected()) && (len > 0)) { - line = readStringUntil('\n'); - len -= line.length(); - // Content length will include all '\n' terminating characters - // Decrement once more to account for the '\n' line ending character - --len; - - if (_printResponseBody) - Serial.println(line); - - _myResponse.body += line; - _myResponse.body += '\n'; - - } -} - -// Ref: http://mihai.ibanescu.net/chunked-encoding-and-python-requests -// http://fssnip.net/2t -void HTTPSRedirect::fetchBodyChunked(void){ - String line; - int chunkSize; - - while (connected()){ - line = readStringUntil('\n'); - - // Skip any empty lines - if (line == "\r") - continue; - - // Chunk sizes are in hexadecimal so convert to integer - chunkSize = (uint32_t) strtol((const char *) line.c_str(), NULL, 16); - DPRINT("Chunk Size: "); - DPRINTLN(chunkSize); - - // Terminating chunk is of size 0 - if (chunkSize == 0) - break; - - while (chunkSize > 0){ - line = readStringUntil('\n'); - if (_printResponseBody) - Serial.println(line); - - _myResponse.body += line; - _myResponse.body += '\n'; - - chunkSize -= line.length(); - // The line above includes the '\r' character - // which is not part of chunk size, so account for it - --chunkSize; - } - - // Skip over chunk trailer - - } - - return; - -} - -unsigned int HTTPSRedirect::getResponseStatus(void){ - // Read response status line - // ref: https://www.tutorialspoint.com/http/http_responses.htm - - unsigned int statusCode; - String reasonPhrase; - String line; - - unsigned int pos = -1; - unsigned int pos2 = -1; - - // Skip any empty lines - do{ - line = readStringUntil('\n'); - }while(line.length() == 0); - - pos = line.indexOf("HTTP/1.1 "); - pos2 = line.indexOf(" ", 9); - - if (!pos){ - statusCode = line.substring(9, pos2).toInt(); - reasonPhrase = line.substring(pos2+1, line.length()-1); - } - else{ - DPRINTLN("Error! No valid Status Code found in HTTP Response."); - statusCode = 0; - reasonPhrase = ""; - } - - _myResponse.statusCode = statusCode; - _myResponse.reasonPhrase = reasonPhrase; - - DPRINT("Status code: "); - DPRINTLN(statusCode); - DPRINT("Reason phrase: "); - DPRINTLN(reasonPhrase); - - return statusCode; -} - -bool HTTPSRedirect::GET(const String& url, const char* host){ - return GET(url, host, _printResponseBody); -} - -bool HTTPSRedirect::GET(const String& url, const char* host, const bool& disp){ - bool retval; - bool oldval; - - // set _printResponseBody temporarily to argument passed - oldval = _printResponseBody; - _printResponseBody = disp; - - // redirected Host and Url need to be initialized in case a - // reConnectFinalEndpoint() request is made after an initial request - // which did not have redirection - _redirHost = host; - _redirUrl = url; - - InitResponse(); - - // Create request packet - createGetRequest(url, host); - - // Calll request handler - retval = printRedir(); - - _printResponseBody = oldval; - return retval; -} - -bool HTTPSRedirect::POST(const String& url, const char* host, const String& payload){ - return POST(url, host, payload, _printResponseBody); -} - -bool HTTPSRedirect::POST(const String& url, const char* host, const String& payload, const bool& disp){ - bool retval; - bool oldval; - - // set _printResponseBody temporarily to argument passed - oldval = _printResponseBody; - _printResponseBody = disp; - - // redirected Host and Url need to be initialized in case a - // reConnectFinalEndpoint() request is made after an initial request - // which did not have redirection - _redirHost = host; - _redirUrl = url; - - InitResponse(); - - // Create request packet - createPostRequest(url, host, payload); - - // Call request handler - retval = printRedir(); - - _printResponseBody = oldval; - return retval; -} - -void HTTPSRedirect::InitResponse(void){ - // Init response data - _myResponse.body = ""; - _myResponse.statusCode = 0; - _myResponse.reasonPhrase = ""; - _myResponse.redirected = false; -} - -int HTTPSRedirect::getStatusCode(void){ - return _myResponse.statusCode; -} - -String HTTPSRedirect::getReasonPhrase(void){ - return _myResponse.reasonPhrase; -} - -String HTTPSRedirect::getResponseBody(void){ - return _myResponse.body; -} - -void HTTPSRedirect::setPrintResponseBody(bool disp){ - _printResponseBody = disp; -} - -void HTTPSRedirect::setMaxRedirects(const unsigned int n){ - _maxRedirects = n; // to-do: use this in code above -} - -void HTTPSRedirect::setContentTypeHeader(const char *type){ - _contentTypeHeader = type; -} - -#ifdef OPTIMIZE_SPEED -bool HTTPSRedirect::reConnectFinalEndpoint(void){ - // disconnect if connection already exists - if (connected()) - stop(); - - DPRINT("_redirHost: "); - DPRINTLN(_redirHost); - DPRINT("_redirUrl: "); - DPRINTLN(_redirUrl); - - // Connect to stored final endpoint - if (!connect(_redirHost.c_str(), _httpsPort)) { - DPRINTLN("Connection to final URL failed!"); - return false; - } - - // Valid request packed must already be - // present at this point in the member variable _Request - // from the previous GET() or POST() request - - // Make call to final endpoint - return printRedir(); -} -#endif - -#ifdef EXTRA_FNS -void HTTPSRedirect::fetchBodyRaw(void){ - String line; - - while (connected()){ - line = readStringUntil('\n'); - if (_printResponseBody) - Serial.println(line); - - _myResponse.body += line; - _myResponse.body += '\n'; - } -} - -void HTTPSRedirect::printHeaderFields(void){ - DPRINT("Transfer Encoding: "); - DPRINTLN(_hF.transferEncoding); - DPRINT("Content Length: "); - DPRINTLN(_hF.contentLength); - DPRINT("Content Type: "); - DPRINTLN(_hF.contentType); -} -#endif - diff --git a/HttpsRedirect.h b/HttpsRedirect.h deleted file mode 100644 index a53afe3..0000000 --- a/HttpsRedirect.h +++ /dev/null @@ -1,89 +0,0 @@ -/* HTTPS on ESP8266 with follow redirects, chunked encoding support - * Version 2.1 - * Author: Sujay Phadke - * Github: @electronicsguy - * Copyright (C) 2017 Sujay Phadke - * All rights reserved. - * - */ -#pragma once -#include - -#define DPRINT(...) -#define DPRINTLN(...) - -// Un-comment for extra functionality -//#define EXTRA_FNS -#define OPTIMIZE_SPEED - -class HTTPSRedirect : public WiFiClientSecure { - private: - const int _httpsPort; - bool _keepAlive; - String _redirUrl; - String _redirHost; - unsigned int _maxRedirects; // to-do - const char* _contentTypeHeader; - - struct headerFields{ - String transferEncoding; - unsigned int contentLength; - #ifdef EXTRA_FNS - String contentType; - #endif - }; - - headerFields _hF; - - String _Request; - - struct Response{ - int statusCode; - String reasonPhrase; - bool redirected; - String body; - }; - - Response _myResponse; - bool _printResponseBody; - - void Init(void); - bool printRedir(void); - void fetchHeader(void); - bool getLocationURL(void); - void fetchBodyUnChunked(unsigned); - void fetchBodyChunked(void); - unsigned int getResponseStatus(void); - void InitResponse(void); - void createGetRequest(const String&, const char*); - void createPostRequest(const String&, const char*, const String&); - -#ifdef EXTRA_FNS - void fetchBodyRaw(void); - void printHeaderFields(void); -#endif - - public: - - HTTPSRedirect(void); - HTTPSRedirect(const int); - ~HTTPSRedirect(); - - bool GET(const String&, const char*); - bool GET(const String&, const char*, const bool&); - bool POST(const String&, const char*, const String&); - bool POST(const String&, const char*, const String&, const bool&); - - int getStatusCode(void); - String getReasonPhrase(void); - String getResponseBody(void); - - void setPrintResponseBody(bool); - void setMaxRedirects(const unsigned int); - - void setContentTypeHeader(const char *); -#ifdef OPTIMIZE_SPEED - bool reConnectFinalEndpoint(void); -#endif - -}; diff --git a/WeatherApp.cpp b/WeatherApp.cpp deleted file mode 100644 index c52dd81..0000000 --- a/WeatherApp.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/**The MIT License (MIT) -Copyright (c) 2017 by Daniel Eichhorn -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, 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: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -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 DEALINGS IN THE -SOFTWARE. -See more at https://blog.squix.org -*/ - - -WeatherApp:WeatherApp() { - -} - -void WeatherApp::updateWeatherData() { - - Serial.println("Allocated"); - delay(1000); - WundergroundConditions *conditionsClient = new WundergroundConditions(IS_METRIC); - conditionsClient->updateConditions(conditions, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY); - delete conditionsClient; - conditionsClient = nullptr; - - drawProgress(70, "Updating forecasts..."); - forecasts = (WGForecast*) malloc(sizeof(WGForecast) * MAX_FORECASTS); - WundergroundForecast *forecastClient = new WundergroundForecast(IS_METRIC); - forecastClient->updateForecast(forecasts, MAX_FORECASTS, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY); - delete forecastClient; - forecastClient = nullptr; - drawProgress(80, "Updating astronomy..."); - - astronomy = (WGAstronomy*) malloc(sizeof(WGAstronomy)); - WundergroundAstronomy *astronomyClient = new WundergroundAstronomy(USE_PM); - astronomyClient->updateAstronomy(astronomy, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY); - delete astronomyClient; - astronomyClient = nullptr; - - drawProgress(100, "Done..."); - Serial.print("Free heap, before bit change: "); - Serial.println(ESP.getFreeHeap()); - gfx.changeBitDepth(2, palette); - - Serial.print("Free heap, after bit change: "); - Serial.println(ESP.getFreeHeap()); - delay(1000); -} - diff --git a/WeatherApp.h b/WeatherApp.h deleted file mode 100644 index aacdcc9..0000000 --- a/WeatherApp.h +++ /dev/null @@ -1,40 +0,0 @@ -/**The MIT License (MIT) -Copyright (c) 2017 by Daniel Eichhorn -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, 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: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -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 DEALINGS IN THE -SOFTWARE. -See more at https://blog.squix.org -*/ - -#pragma once - -#include -#include -#include "HTTPSRedirect.h" - -#define MAX_FORECASTS 12 - -class WeatherApp { - private: - WGConditions conditions; - WGForecast forecasts[MAX_FORECASTS]; - WGAstronomy astronomy; - - public: - WeatherApp(); - void updateWeatherData(); - -} - diff --git a/esp8266-weather-station-color.ino b/esp8266-weather-station-color.ino index 685368a..9f63c05 100644 --- a/esp8266-weather-station-color.ino +++ b/esp8266-weather-station-color.ino @@ -19,29 +19,34 @@ See more at https://blog.squix.org */ #include - #include -#include "MiniGrafx.h" -#include "Carousel.h" -#include "ILI9341_SPI.h" -#include "ArialRounded.h" -#include "moonphases.h" -#include "weathericons.h" - #include +/*** + * Install the following libraries through Arduino Library Manager + * - Mini Grafx by Daniel Eichhorn + * - ESP8266 WeatherStation by Daniel Eichhorn + * - Json Streaming Parser by Daniel Eichhorn + * - simpleDSTadjust by neptune2 + ***/ -#include "settings.h" #include #include #include #include -#include -#include "CalendarParser.h" +#include +#include +#include + +#include "ArialRounded.h" +#include "moonphases.h" +#include "weathericons.h" + +/***************************** + * Important: see settings.h to configure your settings!!! + * ***************************/ +#include "settings.h" -#define TFT_DC D2 -#define TFT_CS D1 -#define TFT_LED D8 #define MINI_BLACK 0 #define MINI_WHITE 1 @@ -58,26 +63,24 @@ uint16_t palette[] = {ILI9341_BLACK, // 0 int SCREEN_WIDTH = 240; int SCREEN_HEIGHT = 320; +// Limited to 4 colors due to memory constraints int BITS_PER_PIXEL = 2; // 2^2 = 4 colors // HOSTNAME for OTA update #define HOSTNAME "ESP8266-OTA-" -/***************************** - * Important: see settings.h to configure your settings!!! - * ***************************/ ILI9341_SPI tft = ILI9341_SPI(TFT_CS, TFT_DC); MiniGrafx gfx = MiniGrafx(&tft, BITS_PER_PIXEL, palette); Carousel carousel(&gfx, 0, 0, 240, 100); -TimeClient timeClient(UTC_OFFSET); - - WGConditions conditions; WGForecast forecasts[MAX_FORECASTS]; WGAstronomy astronomy; +// Setup simpleDSTadjust Library rules +simpleDSTadjust dstAdjusted(StartRule, EndRule); + void updateData(); void drawProgress(uint8_t percentage, String text); void drawTime(); @@ -96,6 +99,8 @@ long lastDownloadUpdate = millis(); void updateCalendar(); +String moonAgeImage = ""; + void setup() { Serial.begin(115200); @@ -112,10 +117,9 @@ void setup() { gfx.setTextAlignment(TEXT_ALIGN_CENTER); gfx.drawString(120, 160, "Connecting to WiFi"); gfx.commit(); + carousel.setFrames(frames, frameCount); - //WiFiManager - Serial.print("Free heap: "); - Serial.println(ESP.getFreeHeap()); + carousel.disableAllIndicators(); //Manual Wifi WiFi.begin("yourssid", "yourpassw0rd"); @@ -123,12 +127,9 @@ void setup() { delay(500); Serial.print("."); } - Serial.println(ESP.getFreeHeap()); - //calendar.updateCalendar(G_SCRIPT_ID); - //updateCalendar(); - // load the weather information + // update the weather information updateData(); } @@ -137,7 +138,6 @@ void loop() { gfx.fillBuffer(MINI_BLACK); drawTime(); drawCurrentWeather(); - //drawForecast(); int remainingTimeBudget = carousel.update(); if (remainingTimeBudget > 0) { @@ -159,58 +159,32 @@ void loop() { // Update the internet based information and update screen void updateData() { - Serial.print("Free heap, before bit change: "); - Serial.println(ESP.getFreeHeap()); - gfx.changeBitDepth(1, palette); - Serial.print("Free heap, after bit change: "); - Serial.println(ESP.getFreeHeap()); + gfx.fillBuffer(MINI_BLACK); gfx.setFont(ArialRoundedMTBold_14); - free(conditions); - free(forecasts); - free(astronomy); - drawProgress(10, "Updating calendar..."); - /*CalendarParser calendar = new Calendar(); - calendar->updateCalendar(G_SCRIPT_ID); - calendar = nullptr;*/ - //updateCalendar(); - drawProgress(20, "Updating time..."); - timeClient.updateTime(); + drawProgress(10, "Updating time..."); + configTime(UTC_OFFSET * 3600, 0, NTP_SERVERS); drawProgress(50, "Updating conditions..."); - conditions = (WGConditions *) malloc(sizeof(WGConditions)); - Serial.println("Allocating"); - delay(1000); - conditions->currentTemp = "hello"; - Serial.println("Allocated"); - delay(1000); WundergroundConditions *conditionsClient = new WundergroundConditions(IS_METRIC); - conditionsClient->updateConditions(conditions, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY); + conditionsClient->updateConditions(&conditions, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY); delete conditionsClient; conditionsClient = nullptr; drawProgress(70, "Updating forecasts..."); - forecasts = (WGForecast*) malloc(sizeof(WGForecast) * MAX_FORECASTS); WundergroundForecast *forecastClient = new WundergroundForecast(IS_METRIC); forecastClient->updateForecast(forecasts, MAX_FORECASTS, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY); delete forecastClient; forecastClient = nullptr; drawProgress(80, "Updating astronomy..."); - astronomy = (WGAstronomy*) malloc(sizeof(WGAstronomy)); - WundergroundAstronomy *astronomyClient = new WundergroundAstronomy(USE_PM); - astronomyClient->updateAstronomy(astronomy, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY); + WundergroundAstronomy *astronomyClient = new WundergroundAstronomy(STYLE_12HR); + astronomyClient->updateAstronomy(&astronomy, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY); delete astronomyClient; astronomyClient = nullptr; + moonAgeImage = String((char) (65 + 26 * (((15 + astronomy.moonAge.toInt()) % 30) / 30.0))); - drawProgress(100, "Done..."); - Serial.print("Free heap, before bit change: "); - Serial.println(ESP.getFreeHeap()); - gfx.changeBitDepth(2, palette); - - Serial.print("Free heap, after bit change: "); - Serial.println(ESP.getFreeHeap()); delay(1000); } @@ -226,90 +200,65 @@ void drawProgress(uint8_t percentage, String text) { gfx.drawRect(10, 165, 240 - 20, 15); gfx.setColor(MINI_BLUE); gfx.fillRect(12, 167, 216 * percentage / 100, 11); - //ui.drawProgressBar(10, 165, 240 - 20, 15, percentage, ILI9341_WHITE, ILI9341_BLUE); + gfx.commit(); } // draws the clock void drawTime() { - gfx.setTextAlignment(TEXT_ALIGN_CENTER); + /*gfx.setTextAlignment(TEXT_ALIGN_CENTER); gfx.setColor(MINI_WHITE); gfx.setFont(ArialRoundedMTBold_14); - String date = conditions->date; + String date = conditions.date; gfx.drawString(120, 6, date); gfx.setFont(ArialRoundedMTBold_36); String time = timeClient.getHours() + ":" + timeClient.getMinutes() + ":" + timeClient.getSeconds(); - gfx.drawString(120, 20, time); -} - -void updateCalendar() { - Serial.print("Free heap: "); - Serial.println(ESP.getFreeHeap()); - const char* host = "script.google.com"; - // Replace with your own script id to make server side changes - const char *GScriptId = "AKfycbwdOi6zab7cLU5fEr0AL6KrAMpygUoFHOtSrgnKfccyHHkpZPo"; + gfx.drawString(120, 20, time);*/ + char *dstAbbrev; + char time_str[11]; + time_t now = dstAdjusted.time(&dstAbbrev); + struct tm * timeinfo = localtime (&now); - const int httpsPort = 443; - String url = String("/macros/s/") + GScriptId + "/exec"; - // echo | openssl s_client -connect script.google.com:443 |& openssl x509 -fingerprint -noout - const char* fingerprint = "08:9E:B2:B8:77:37:3E:85:26:09:CA:29:13:D2:B0:57:26:DE:C4:6D"; + gfx.setTextAlignment(TEXT_ALIGN_CENTER); + gfx.setFont(ArialRoundedMTBold_14); + String date = ctime(&now); + date = date.substring(0,11) + String(1900 + timeinfo->tm_year); + gfx.drawString(120, 6, date); - // Use HTTPSRedirect class to create a new TLS connection - HTTPSRedirect *client = new HTTPSRedirect(httpsPort); - client->setPrintResponseBody(true); - client->setContentTypeHeader("application/json"); - Serial.print("Connecting to "); - Serial.println(host); - - // Try to connect for a maximum of 5 times - bool flag = false; - for (int i=0; i<5; i++){ - Serial.println("."); - delay(1000); - int retval = client->connect(host, httpsPort); - if (retval == 1) { - flag = true; - break; - } - else - Serial.println("Connection failed. Retrying..."); - } - - if (!flag){ - Serial.print("Could not connect to server: "); - Serial.println(host); - Serial.println("Exiting..."); - return; - } + gfx.setFont(ArialRoundedMTBold_36); - if (client->verify(fingerprint, host)) { - Serial.println("Certificate match."); + if (STYLE_12HR) { + int hour = (timeinfo->tm_hour+11)%12+1; // take care of noon and midnight + sprintf(time_str, "%2d:%02d:%02d\n",hour, timeinfo->tm_min, timeinfo->tm_sec); + gfx.drawString(120, 20, time_str); } else { - Serial.println("Certificate mis-match"); + sprintf(time_str, "%02d:%02d:%02d\n",timeinfo->tm_hour, timeinfo->tm_min, timeinfo->tm_sec); + gfx.drawString(120, 20, time_str); } - // fetch spreadsheet data - client->GET(url, host); - - - delete client; - client = nullptr; + gfx.setTextAlignment(TEXT_ALIGN_LEFT); + gfx.setFont(ArialMT_Plain_10); + gfx.setColor(MINI_BLUE); + if (STYLE_12HR) { + sprintf(time_str, "%s\n%s", dstAbbrev, timeinfo->tm_hour>=12?"PM":"AM"); + gfx.drawString(195, 27, time_str); + } else { + sprintf(time_str, "%s", dstAbbrev); + gfx.drawString(195, 27, time_str); // Known bug: Cuts off 4th character of timezone abbreviation + } } // draws current weather information void drawCurrentWeather() { - // Weather Icon - gfx.setTransparentColor(MINI_BLACK); - //gfx.drawBmpFromFile(weatherIcon + ".bmp", 0, 55); - gfx.drawPalettedBitmapFromPgm(0, 55, getMeteoconIconFromProgmem(conditions->weatherIcon)); + gfx.drawPalettedBitmapFromPgm(0, 55, getMeteoconIconFromProgmem(conditions.weatherIcon)); // Weather Text gfx.setFont(ArialRoundedMTBold_14); gfx.setColor(MINI_YELLOW); gfx.setTextAlignment(TEXT_ALIGN_RIGHT); - gfx.drawString(220, 76, conditions->weatherText); + gfx.drawString(220, 76, conditions.weatherText); gfx.setFont(ArialRoundedMTBold_36); gfx.setColor(MINI_WHITE); @@ -318,7 +267,7 @@ void drawCurrentWeather() { if (IS_METRIC) { degreeSign = "°C"; } - String temp = conditions->currentTemp + degreeSign; + String temp = conditions.currentTemp + degreeSign; gfx.drawString(220, 89, temp); } @@ -328,15 +277,13 @@ void drawForecast1(MiniGrafx *display, CarouselState* state, int16_t x, int16_t drawForecastDetail(x + 95, y + 165, 2); drawForecastDetail(x + 180, y + 165, 4); } + void drawForecast2(MiniGrafx *display, CarouselState* state, int16_t x, int16_t y) { drawForecastDetail(x + 10, y + 165, 6); drawForecastDetail(x + 95, y + 165, 8); drawForecastDetail(x + 180, y + 165, 10); } -// draws the three forecast columns -void drawForecast() { -} // helper for the forecast columns void drawForecastDetail(uint16_t x, uint16_t y, uint8_t dayIndex) { @@ -351,39 +298,36 @@ void drawForecastDetail(uint16_t x, uint16_t y, uint8_t dayIndex) { gfx.drawString(x + 25, y, forecasts[dayIndex].forecastLowTemp + "|" + forecasts[dayIndex].forecastHighTemp); gfx.drawPalettedBitmapFromPgm(x, y + 15, getMiniMeteoconIconFromProgmem(forecasts[dayIndex].forecastIcon)); + gfx.setColor(MINI_BLUE); + gfx.drawString(x + 25, y + 60, forecasts[dayIndex].PoP + "%"); } // draw moonphase and sunrise/set and moonrise/set void drawAstronomy() { - char moonAgeImage = 65 + 26 * astronomy->moonAge.toInt() / 30.0; + gfx.setFont(MoonPhases_Regular_36); gfx.setColor(MINI_WHITE); gfx.setTextAlignment(TEXT_ALIGN_CENTER); - gfx.drawString(120, 270, String(moonAgeImage)); - //gfx.drawBmpFromFile("/moon" + String(moonAgeImage) + ".bmp", 120 - 30, 255); + gfx.drawString(120, 275, moonAgeImage); gfx.setColor(MINI_WHITE); gfx.setFont(ArialRoundedMTBold_14); gfx.setTextAlignment(TEXT_ALIGN_CENTER); gfx.setColor(MINI_YELLOW); - gfx.drawString(120, 245, astronomy->moonPhase); + gfx.drawString(120, 250, astronomy.moonPhase); gfx.setTextAlignment(TEXT_ALIGN_LEFT); gfx.setColor(MINI_YELLOW); - gfx.drawString(10, 245, "Sun"); + gfx.drawString(5, 250, "Sun"); gfx.setColor(MINI_WHITE); - astronomy->sunriseTime.trim(); - astronomy->sunriseTime.trim(); - gfx.drawString(10, 276, astronomy->sunriseTime); - gfx.drawString(10, 291, astronomy->sunsetTime); + gfx.drawString(5, 276, astronomy.sunriseTime); + gfx.drawString(5, 291, astronomy.sunsetTime); gfx.setTextAlignment(TEXT_ALIGN_RIGHT); gfx.setColor(MINI_YELLOW); - gfx.drawString(230, 245, "Moon"); + gfx.drawString(235, 250, "Moon"); gfx.setColor(MINI_WHITE); - astronomy->moonriseTime.trim(); - astronomy->moonsetTime.trim(); - gfx.drawString(230, 276, astronomy->moonriseTime); - gfx.drawString(230, 291, astronomy->moonsetTime); + gfx.drawString(235, 276, astronomy.moonriseTime); + gfx.drawString(235, 291, astronomy.moonsetTime); } @@ -438,8 +382,4 @@ const char* getMiniMeteoconIconFromProgmem(String iconText) { return miniunknown; } -// if you want separators, uncomment the tft-line -void drawSeparator(uint16_t y) { - //tft.drawFastHLine(10, y, 240 - 2 * 10, 0x4228); -} diff --git a/settings.h b/settings.h index 6d379cb..0705900 100644 --- a/settings.h +++ b/settings.h @@ -18,34 +18,45 @@ SOFTWARE. See more at http://blog.squix.ch */ +#include + // Setup const int UPDATE_INTERVAL_SECS = 10 * 60; // Update every 10 minutes // Pins for the ILI9341 #define TFT_DC D2 #define TFT_CS D1 -#define LED_PIN D8 +#define TFT_LED D8 -String G_SCRIPT_ID = "AKfycbwdOi6zab7cLU5fEr0AL6KrAMpygUoFHOtSrgnKfccyHHkpZPo"; -#define USE_PM false -// TimeClient settings -const float UTC_OFFSET = 2; // Wunderground Settings +// To check your settings first try them out in your browser: +// http://api.wunderground.com/api/WUNDERGROUND_API_KEY/conditions/q/WUNDERGROUND_COUNTTRY/WUNDERGROUND_CITY.json +// e.g. http://api.wunderground.com/api/808ba87ed77c4511/conditions/q/CH/Zurich.json +// e.g. http://api.wunderground.com/api/808ba87ed77c4511/conditions/q/CA/SAN_FRANCISCO.json <- note that in the US you use the state instead of country code const boolean IS_METRIC = true; const String WUNDERGRROUND_API_KEY = "808ba87ed77c4501"; const String WUNDERGRROUND_LANGUAGE = "EN"; const String WUNDERGROUND_COUNTRY = "CH"; const String WUNDERGROUND_CITY = "Zurich"; -//Thingspeak Settings -const String THINGSPEAK_CHANNEL_ID = "67284"; -const String THINGSPEAK_API_READ_KEY = "L2VIW20QVNZJBLAK"; +#define UTC_OFFSET + 1 +struct dstRule StartRule = {"CEST", Last, Sun, Mar, 2, 3600}; // Central European Summer Time = UTC/GMT +2 hours +struct dstRule EndRule = {"CET", Last, Sun, Oct, 2, 0}; // Central European Time = UTC/GMT +1 hour -// List, so that the downloader knows what to fetch -String wundergroundIcons [] = {"chanceflurries","chancerain","chancesleet","chancesnow","clear","cloudy","flurries","fog","hazy","mostlycloudy","mostlysunny","partlycloudy","partlysunny","rain","sleet","snow","sunny","tstorms","unknown"}; +// Settings for Boston +// #define UTC_OFFSET -5 +// struct dstRule StartRule = {"EDT", Second, Sun, Mar, 2, 3600}; // Eastern Daylight time = UTC/GMT -4 hours +// struct dstRule EndRule = {"EST", First, Sun, Nov, 1, 0}; // Eastern Standard time = UTC/GMT -5 hour + +// Change for 12 Hour/ 24 hour style clock +#define STYLE_12HR false + +// change for different ntp (time servers) +#define NTP_SERVERS "0.ch.pool.ntp.org", "1.ch.pool.ntp.org", "2.ch.pool.ntp.org" +// #define NTP_SERVERS "us.pool.ntp.org", "time.nist.gov", "pool.ntp.org" /*************************** * End Settings