Cleaning up sample code

This commit is contained in:
Daniel Eichhorn
2017-06-25 13:05:16 +02:00
parent eb560a2797
commit 7459475ffa
8 changed files with 101 additions and 1045 deletions

View File

@@ -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 <ESP8266WiFi.h>
#include <WiFiClient.h>
#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() {
}

View File

@@ -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 <JsonListener.h>
#include <JsonStreamingParser.h>
#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();
};

View File

@@ -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 <electronicsguy123@gmail.com>
* 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<s>://<hostname>/<url>
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

View File

@@ -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 <electronicsguy123@gmail.com>
* All rights reserved.
*
*/
#pragma once
#include <WiFiClientSecure.h>
#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
};

View File

@@ -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);
}

View File

@@ -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 <JsonListener.h>
#include <JsonStreamingParser.h>
#include "HTTPSRedirect.h"
#define MAX_FORECASTS 12
class WeatherApp {
private:
WGConditions conditions;
WGForecast forecasts[MAX_FORECASTS];
WGAstronomy astronomy;
public:
WeatherApp();
void updateWeatherData();
}

View File

@@ -19,29 +19,34 @@ See more at https://blog.squix.org
*/ */
#include <Arduino.h> #include <Arduino.h>
#include <SPI.h> #include <SPI.h>
#include "MiniGrafx.h"
#include "Carousel.h"
#include "ILI9341_SPI.h"
#include "ArialRounded.h"
#include "moonphases.h"
#include "weathericons.h"
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
/***
* 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 <JsonListener.h> #include <JsonListener.h>
#include <WundergroundConditions.h> #include <WundergroundConditions.h>
#include <WundergroundForecast.h> #include <WundergroundForecast.h>
#include <WundergroundAstronomy.h> #include <WundergroundAstronomy.h>
#include <TimeClient.h> #include <MiniGrafx.h>
#include "CalendarParser.h" #include <Carousel.h>
#include <ILI9341_SPI.h>
#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_BLACK 0
#define MINI_WHITE 1 #define MINI_WHITE 1
@@ -58,26 +63,24 @@ uint16_t palette[] = {ILI9341_BLACK, // 0
int SCREEN_WIDTH = 240; int SCREEN_WIDTH = 240;
int SCREEN_HEIGHT = 320; int SCREEN_HEIGHT = 320;
// Limited to 4 colors due to memory constraints
int BITS_PER_PIXEL = 2; // 2^2 = 4 colors int BITS_PER_PIXEL = 2; // 2^2 = 4 colors
// HOSTNAME for OTA update // HOSTNAME for OTA update
#define HOSTNAME "ESP8266-OTA-" #define HOSTNAME "ESP8266-OTA-"
/*****************************
* Important: see settings.h to configure your settings!!!
* ***************************/
ILI9341_SPI tft = ILI9341_SPI(TFT_CS, TFT_DC); ILI9341_SPI tft = ILI9341_SPI(TFT_CS, TFT_DC);
MiniGrafx gfx = MiniGrafx(&tft, BITS_PER_PIXEL, palette); MiniGrafx gfx = MiniGrafx(&tft, BITS_PER_PIXEL, palette);
Carousel carousel(&gfx, 0, 0, 240, 100); Carousel carousel(&gfx, 0, 0, 240, 100);
TimeClient timeClient(UTC_OFFSET);
WGConditions conditions; WGConditions conditions;
WGForecast forecasts[MAX_FORECASTS]; WGForecast forecasts[MAX_FORECASTS];
WGAstronomy astronomy; WGAstronomy astronomy;
// Setup simpleDSTadjust Library rules
simpleDSTadjust dstAdjusted(StartRule, EndRule);
void updateData(); void updateData();
void drawProgress(uint8_t percentage, String text); void drawProgress(uint8_t percentage, String text);
void drawTime(); void drawTime();
@@ -96,6 +99,8 @@ long lastDownloadUpdate = millis();
void updateCalendar(); void updateCalendar();
String moonAgeImage = "";
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
@@ -112,10 +117,9 @@ void setup() {
gfx.setTextAlignment(TEXT_ALIGN_CENTER); gfx.setTextAlignment(TEXT_ALIGN_CENTER);
gfx.drawString(120, 160, "Connecting to WiFi"); gfx.drawString(120, 160, "Connecting to WiFi");
gfx.commit(); gfx.commit();
carousel.setFrames(frames, frameCount); carousel.setFrames(frames, frameCount);
//WiFiManager carousel.disableAllIndicators();
Serial.print("Free heap: ");
Serial.println(ESP.getFreeHeap());
//Manual Wifi //Manual Wifi
WiFi.begin("yourssid", "yourpassw0rd"); WiFi.begin("yourssid", "yourpassw0rd");
@@ -123,12 +127,9 @@ void setup() {
delay(500); delay(500);
Serial.print("."); Serial.print(".");
} }
Serial.println(ESP.getFreeHeap());
//calendar.updateCalendar(G_SCRIPT_ID);
//updateCalendar();
// load the weather information // update the weather information
updateData(); updateData();
} }
@@ -137,7 +138,6 @@ void loop() {
gfx.fillBuffer(MINI_BLACK); gfx.fillBuffer(MINI_BLACK);
drawTime(); drawTime();
drawCurrentWeather(); drawCurrentWeather();
//drawForecast();
int remainingTimeBudget = carousel.update(); int remainingTimeBudget = carousel.update();
if (remainingTimeBudget > 0) { if (remainingTimeBudget > 0) {
@@ -159,58 +159,32 @@ void loop() {
// Update the internet based information and update screen // Update the internet based information and update screen
void updateData() { 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.fillBuffer(MINI_BLACK);
gfx.setFont(ArialRoundedMTBold_14); 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..."); drawProgress(10, "Updating time...");
timeClient.updateTime(); configTime(UTC_OFFSET * 3600, 0, NTP_SERVERS);
drawProgress(50, "Updating conditions..."); 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); 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; delete conditionsClient;
conditionsClient = nullptr; conditionsClient = nullptr;
drawProgress(70, "Updating forecasts..."); drawProgress(70, "Updating forecasts...");
forecasts = (WGForecast*) malloc(sizeof(WGForecast) * MAX_FORECASTS);
WundergroundForecast *forecastClient = new WundergroundForecast(IS_METRIC); WundergroundForecast *forecastClient = new WundergroundForecast(IS_METRIC);
forecastClient->updateForecast(forecasts, MAX_FORECASTS, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY); forecastClient->updateForecast(forecasts, MAX_FORECASTS, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY);
delete forecastClient; delete forecastClient;
forecastClient = nullptr; forecastClient = nullptr;
drawProgress(80, "Updating astronomy..."); drawProgress(80, "Updating astronomy...");
astronomy = (WGAstronomy*) malloc(sizeof(WGAstronomy)); WundergroundAstronomy *astronomyClient = new WundergroundAstronomy(STYLE_12HR);
WundergroundAstronomy *astronomyClient = new WundergroundAstronomy(USE_PM); astronomyClient->updateAstronomy(&astronomy, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY);
astronomyClient->updateAstronomy(astronomy, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY);
delete astronomyClient; delete astronomyClient;
astronomyClient = nullptr; 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); delay(1000);
} }
@@ -226,90 +200,65 @@ void drawProgress(uint8_t percentage, String text) {
gfx.drawRect(10, 165, 240 - 20, 15); gfx.drawRect(10, 165, 240 - 20, 15);
gfx.setColor(MINI_BLUE); gfx.setColor(MINI_BLUE);
gfx.fillRect(12, 167, 216 * percentage / 100, 11); gfx.fillRect(12, 167, 216 * percentage / 100, 11);
//ui.drawProgressBar(10, 165, 240 - 20, 15, percentage, ILI9341_WHITE, ILI9341_BLUE);
gfx.commit(); gfx.commit();
} }
// draws the clock // draws the clock
void drawTime() { void drawTime() {
gfx.setTextAlignment(TEXT_ALIGN_CENTER); /*gfx.setTextAlignment(TEXT_ALIGN_CENTER);
gfx.setColor(MINI_WHITE); gfx.setColor(MINI_WHITE);
gfx.setFont(ArialRoundedMTBold_14); gfx.setFont(ArialRoundedMTBold_14);
String date = conditions->date; String date = conditions.date;
gfx.drawString(120, 6, date); gfx.drawString(120, 6, date);
gfx.setFont(ArialRoundedMTBold_36); gfx.setFont(ArialRoundedMTBold_36);
String time = timeClient.getHours() + ":" + timeClient.getMinutes() + ":" + timeClient.getSeconds(); String time = timeClient.getHours() + ":" + timeClient.getMinutes() + ":" + timeClient.getSeconds();
gfx.drawString(120, 20, time); gfx.drawString(120, 20, time);*/
} char *dstAbbrev;
char time_str[11];
time_t now = dstAdjusted.time(&dstAbbrev);
struct tm * timeinfo = localtime (&now);
void updateCalendar() { gfx.setTextAlignment(TEXT_ALIGN_CENTER);
Serial.print("Free heap: "); gfx.setFont(ArialRoundedMTBold_14);
Serial.println(ESP.getFreeHeap()); String date = ctime(&now);
const char* host = "script.google.com"; date = date.substring(0,11) + String(1900 + timeinfo->tm_year);
// Replace with your own script id to make server side changes gfx.drawString(120, 6, date);
const char *GScriptId = "AKfycbwdOi6zab7cLU5fEr0AL6KrAMpygUoFHOtSrgnKfccyHHkpZPo";
const int httpsPort = 443; gfx.setFont(ArialRoundedMTBold_36);
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";
// Use HTTPSRedirect class to create a new TLS connection if (STYLE_12HR) {
HTTPSRedirect *client = new HTTPSRedirect(httpsPort); int hour = (timeinfo->tm_hour+11)%12+1; // take care of noon and midnight
client->setPrintResponseBody(true); sprintf(time_str, "%2d:%02d:%02d\n",hour, timeinfo->tm_min, timeinfo->tm_sec);
client->setContentTypeHeader("application/json"); gfx.drawString(120, 20, time_str);
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;
}
if (client->verify(fingerprint, host)) {
Serial.println("Certificate match.");
} else { } 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 gfx.setTextAlignment(TEXT_ALIGN_LEFT);
client->GET(url, host); gfx.setFont(ArialMT_Plain_10);
gfx.setColor(MINI_BLUE);
if (STYLE_12HR) {
delete client; sprintf(time_str, "%s\n%s", dstAbbrev, timeinfo->tm_hour>=12?"PM":"AM");
client = nullptr; 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 // draws current weather information
void drawCurrentWeather() { void drawCurrentWeather() {
// Weather Icon
gfx.setTransparentColor(MINI_BLACK); 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 // Weather Text
gfx.setFont(ArialRoundedMTBold_14); gfx.setFont(ArialRoundedMTBold_14);
gfx.setColor(MINI_YELLOW); gfx.setColor(MINI_YELLOW);
gfx.setTextAlignment(TEXT_ALIGN_RIGHT); gfx.setTextAlignment(TEXT_ALIGN_RIGHT);
gfx.drawString(220, 76, conditions->weatherText); gfx.drawString(220, 76, conditions.weatherText);
gfx.setFont(ArialRoundedMTBold_36); gfx.setFont(ArialRoundedMTBold_36);
gfx.setColor(MINI_WHITE); gfx.setColor(MINI_WHITE);
@@ -318,7 +267,7 @@ void drawCurrentWeather() {
if (IS_METRIC) { if (IS_METRIC) {
degreeSign = "°C"; degreeSign = "°C";
} }
String temp = conditions->currentTemp + degreeSign; String temp = conditions.currentTemp + degreeSign;
gfx.drawString(220, 89, temp); 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 + 95, y + 165, 2);
drawForecastDetail(x + 180, y + 165, 4); drawForecastDetail(x + 180, y + 165, 4);
} }
void drawForecast2(MiniGrafx *display, CarouselState* state, int16_t x, int16_t y) { void drawForecast2(MiniGrafx *display, CarouselState* state, int16_t x, int16_t y) {
drawForecastDetail(x + 10, y + 165, 6); drawForecastDetail(x + 10, y + 165, 6);
drawForecastDetail(x + 95, y + 165, 8); drawForecastDetail(x + 95, y + 165, 8);
drawForecastDetail(x + 180, y + 165, 10); drawForecastDetail(x + 180, y + 165, 10);
} }
// draws the three forecast columns
void drawForecast() {
}
// helper for the forecast columns // helper for the forecast columns
void drawForecastDetail(uint16_t x, uint16_t y, uint8_t dayIndex) { 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.drawString(x + 25, y, forecasts[dayIndex].forecastLowTemp + "|" + forecasts[dayIndex].forecastHighTemp);
gfx.drawPalettedBitmapFromPgm(x, y + 15, getMiniMeteoconIconFromProgmem(forecasts[dayIndex].forecastIcon)); 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 // draw moonphase and sunrise/set and moonrise/set
void drawAstronomy() { void drawAstronomy() {
char moonAgeImage = 65 + 26 * astronomy->moonAge.toInt() / 30.0;
gfx.setFont(MoonPhases_Regular_36); gfx.setFont(MoonPhases_Regular_36);
gfx.setColor(MINI_WHITE); gfx.setColor(MINI_WHITE);
gfx.setTextAlignment(TEXT_ALIGN_CENTER); gfx.setTextAlignment(TEXT_ALIGN_CENTER);
gfx.drawString(120, 270, String(moonAgeImage)); gfx.drawString(120, 275, moonAgeImage);
//gfx.drawBmpFromFile("/moon" + String(moonAgeImage) + ".bmp", 120 - 30, 255);
gfx.setColor(MINI_WHITE); gfx.setColor(MINI_WHITE);
gfx.setFont(ArialRoundedMTBold_14); gfx.setFont(ArialRoundedMTBold_14);
gfx.setTextAlignment(TEXT_ALIGN_CENTER); gfx.setTextAlignment(TEXT_ALIGN_CENTER);
gfx.setColor(MINI_YELLOW); gfx.setColor(MINI_YELLOW);
gfx.drawString(120, 245, astronomy->moonPhase); gfx.drawString(120, 250, astronomy.moonPhase);
gfx.setTextAlignment(TEXT_ALIGN_LEFT); gfx.setTextAlignment(TEXT_ALIGN_LEFT);
gfx.setColor(MINI_YELLOW); gfx.setColor(MINI_YELLOW);
gfx.drawString(10, 245, "Sun"); gfx.drawString(5, 250, "Sun");
gfx.setColor(MINI_WHITE); gfx.setColor(MINI_WHITE);
astronomy->sunriseTime.trim(); gfx.drawString(5, 276, astronomy.sunriseTime);
astronomy->sunriseTime.trim(); gfx.drawString(5, 291, astronomy.sunsetTime);
gfx.drawString(10, 276, astronomy->sunriseTime);
gfx.drawString(10, 291, astronomy->sunsetTime);
gfx.setTextAlignment(TEXT_ALIGN_RIGHT); gfx.setTextAlignment(TEXT_ALIGN_RIGHT);
gfx.setColor(MINI_YELLOW); gfx.setColor(MINI_YELLOW);
gfx.drawString(230, 245, "Moon"); gfx.drawString(235, 250, "Moon");
gfx.setColor(MINI_WHITE); gfx.setColor(MINI_WHITE);
astronomy->moonriseTime.trim(); gfx.drawString(235, 276, astronomy.moonriseTime);
astronomy->moonsetTime.trim(); gfx.drawString(235, 291, astronomy.moonsetTime);
gfx.drawString(230, 276, astronomy->moonriseTime);
gfx.drawString(230, 291, astronomy->moonsetTime);
} }
@@ -438,8 +382,4 @@ const char* getMiniMeteoconIconFromProgmem(String iconText) {
return miniunknown; return miniunknown;
} }
// if you want separators, uncomment the tft-line
void drawSeparator(uint16_t y) {
//tft.drawFastHLine(10, y, 240 - 2 * 10, 0x4228);
}

View File

@@ -18,34 +18,45 @@ SOFTWARE.
See more at http://blog.squix.ch See more at http://blog.squix.ch
*/ */
#include <simpleDSTadjust.h>
// Setup // Setup
const int UPDATE_INTERVAL_SECS = 10 * 60; // Update every 10 minutes const int UPDATE_INTERVAL_SECS = 10 * 60; // Update every 10 minutes
// Pins for the ILI9341 // Pins for the ILI9341
#define TFT_DC D2 #define TFT_DC D2
#define TFT_CS D1 #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 // 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 boolean IS_METRIC = true;
const String WUNDERGRROUND_API_KEY = "808ba87ed77c4501"; const String WUNDERGRROUND_API_KEY = "808ba87ed77c4501";
const String WUNDERGRROUND_LANGUAGE = "EN"; const String WUNDERGRROUND_LANGUAGE = "EN";
const String WUNDERGROUND_COUNTRY = "CH"; const String WUNDERGROUND_COUNTRY = "CH";
const String WUNDERGROUND_CITY = "Zurich"; const String WUNDERGROUND_CITY = "Zurich";
//Thingspeak Settings #define UTC_OFFSET + 1
const String THINGSPEAK_CHANNEL_ID = "67284"; struct dstRule StartRule = {"CEST", Last, Sun, Mar, 2, 3600}; // Central European Summer Time = UTC/GMT +2 hours
const String THINGSPEAK_API_READ_KEY = "L2VIW20QVNZJBLAK"; 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 // Settings for Boston
String wundergroundIcons [] = {"chanceflurries","chancerain","chancesleet","chancesnow","clear","cloudy","flurries","fog","hazy","mostlycloudy","mostlysunny","partlycloudy","partlysunny","rain","sleet","snow","sunny","tstorms","unknown"}; // #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 * End Settings