The Icon for the last Moonphase is missing. Serial Console shows /moon23.bmp is missing. Just changed one Line to get the last icon fetched.
344 lines
11 KiB
C++
344 lines
11 KiB
C++
/**The MIT License (MIT)
|
|
Copyright (c) 2015 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.ch
|
|
*/
|
|
|
|
#include <Arduino.h>
|
|
|
|
#include <Adafruit_GFX.h> // Core graphics library
|
|
#include <Adafruit_ILI9341.h> // Hardware-specific library
|
|
#include <SPI.h>
|
|
// Additional UI functions
|
|
#include "GfxUi.h"
|
|
|
|
// Fonts created by http://oleddisplay.squix.ch/
|
|
#include "ArialRoundedMTBold_14.h"
|
|
#include "ArialRoundedMTBold_36.h"
|
|
|
|
// Download helper
|
|
#include "WebResource.h"
|
|
|
|
#include <ESP8266WiFi.h>
|
|
#include <ArduinoOTA.h>
|
|
#include <ESP8266mDNS.h>
|
|
#include <DNSServer.h>
|
|
#include <ESP8266WebServer.h>
|
|
|
|
// Helps with connecting to internet
|
|
#include <WiFiManager.h>
|
|
|
|
// check settings.h for adapting to your needs
|
|
#include "settings.h"
|
|
#include <JsonListener.h>
|
|
#include <WundergroundClient.h>
|
|
#include "TimeClient.h"
|
|
|
|
// HOSTNAME for OTA update
|
|
#define HOSTNAME "ESP8266-OTA-"
|
|
|
|
/*****************************
|
|
* Important: see settings.h to configure your settings!!!
|
|
* ***************************/
|
|
|
|
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);
|
|
GfxUi ui = GfxUi(&tft);
|
|
|
|
|
|
WebResource webResource;
|
|
TimeClient timeClient(UTC_OFFSET);
|
|
|
|
// Set to false, if you prefere imperial/inches, Fahrenheit
|
|
WundergroundClient wunderground(IS_METRIC);
|
|
|
|
//declaring prototypes
|
|
void configModeCallback (WiFiManager *myWiFiManager);
|
|
void downloadCallback(String filename, int16_t bytesDownloaded, int16_t bytesTotal);
|
|
ProgressCallback _downloadCallback = downloadCallback;
|
|
void downloadResources();
|
|
void updateData();
|
|
void drawProgress(uint8_t percentage, String text);
|
|
void drawTime();
|
|
void drawCurrentWeather();
|
|
void drawForecast();
|
|
void drawForecastDetail(uint16_t x, uint16_t y, uint8_t dayIndex);
|
|
String getMeteoconIcon(String iconText);
|
|
void drawAstronomy();
|
|
void drawSeparator(uint16_t y);
|
|
|
|
long lastDownloadUpdate = millis();
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
tft.begin();
|
|
tft.fillScreen(ILI9341_BLACK);
|
|
tft.setFont(&ArialRoundedMTBold_14);
|
|
ui.setTextColor(ILI9341_ORANGE, ILI9341_BLACK);
|
|
ui.setTextAlignment(CENTER);
|
|
ui.drawString(120, 160, "Connecting to WiFi");
|
|
|
|
//WiFiManager
|
|
//Local intialization. Once its business is done, there is no need to keep it around
|
|
WiFiManager wifiManager;
|
|
// Uncomment for testing wifi manager
|
|
//wifiManager.resetSettings();
|
|
wifiManager.setAPCallback(configModeCallback);
|
|
|
|
//or use this for auto generated name ESP + ChipID
|
|
wifiManager.autoConnect();
|
|
|
|
//Manual Wifi
|
|
//WiFi.begin(WIFI_SSID, WIFI_PWD);
|
|
|
|
// OTA Setup
|
|
String hostname(HOSTNAME);
|
|
hostname += String(ESP.getChipId(), HEX);
|
|
WiFi.hostname(hostname);
|
|
ArduinoOTA.setHostname((const char *)hostname.c_str());
|
|
ArduinoOTA.begin();
|
|
SPIFFS.begin();
|
|
|
|
//Uncomment if you want to update all internet resources
|
|
//SPIFFS.format();
|
|
|
|
// download images from the net. If images already exist don't download
|
|
downloadResources();
|
|
|
|
// load the weather information
|
|
updateData();
|
|
}
|
|
|
|
long lastDrew = 0;
|
|
void loop() {
|
|
// Handle OTA update requests
|
|
ArduinoOTA.handle();
|
|
|
|
// Check if we should update the clock
|
|
if (millis() - lastDrew > 30000 && wunderground.getSeconds() == "00") {
|
|
drawTime();
|
|
lastDrew = millis();
|
|
}
|
|
|
|
// Check if we should update weather information
|
|
if (millis() - lastDownloadUpdate > 1000 * UPDATE_INTERVAL_SECS) {
|
|
updateData();
|
|
lastDownloadUpdate = millis();
|
|
}
|
|
}
|
|
|
|
// Called if WiFi has not been configured yet
|
|
void configModeCallback (WiFiManager *myWiFiManager) {
|
|
ui.setTextAlignment(CENTER);
|
|
tft.setFont(&ArialRoundedMTBold_14);
|
|
tft.setTextColor(ILI9341_ORANGE);
|
|
ui.drawString(120, 28, "Wifi Manager");
|
|
ui.drawString(120, 42, "Please connect to AP");
|
|
tft.setTextColor(ILI9341_WHITE);
|
|
ui.drawString(120, 56, myWiFiManager->getConfigPortalSSID());
|
|
tft.setTextColor(ILI9341_ORANGE);
|
|
ui.drawString(120, 70, "To setup Wifi Configuration");
|
|
}
|
|
|
|
// callback called during download of files. Updates progress bar
|
|
void downloadCallback(String filename, int16_t bytesDownloaded, int16_t bytesTotal) {
|
|
Serial.println(String(bytesDownloaded) + " / " + String(bytesTotal));
|
|
|
|
int percentage = 100 * bytesDownloaded / bytesTotal;
|
|
if (percentage == 0) {
|
|
ui.drawString(120, 160, filename);
|
|
}
|
|
if (percentage % 5 == 0) {
|
|
ui.setTextAlignment(CENTER);
|
|
ui.setTextColor(ILI9341_ORANGE, ILI9341_BLACK);
|
|
//ui.drawString(120, 160, String(percentage) + "%");
|
|
ui.drawProgressBar(10, 165, 240 - 20, 15, percentage, ILI9341_WHITE, ILI9341_BLUE);
|
|
}
|
|
|
|
}
|
|
|
|
// Download the bitmaps
|
|
void downloadResources() {
|
|
tft.fillScreen(ILI9341_BLACK);
|
|
tft.setFont(&ArialRoundedMTBold_14);
|
|
char id[5];
|
|
for (int i = 0; i < 21; i++) {
|
|
sprintf(id, "%02d", i);
|
|
tft.fillRect(0, 120, 240, 40, ILI9341_BLACK);
|
|
webResource.downloadFile("http://www.squix.org/blog/wunderground/" + wundergroundIcons[i] + ".bmp", wundergroundIcons[i] + ".bmp", _downloadCallback);
|
|
}
|
|
for (int i = 0; i < 21; i++) {
|
|
sprintf(id, "%02d", i);
|
|
tft.fillRect(0, 120, 240, 40, ILI9341_BLACK);
|
|
webResource.downloadFile("http://www.squix.org/blog/wunderground/mini/" + wundergroundIcons[i] + ".bmp", "/mini/" + wundergroundIcons[i] + ".bmp", _downloadCallback);
|
|
}
|
|
for (int i = 0; i < 24; i++) {
|
|
tft.fillRect(0, 120, 240, 40, ILI9341_BLACK);
|
|
webResource.downloadFile("http://www.squix.org/blog/moonphase_L" + String(i) + ".bmp", "/moon" + String(i) + ".bmp", _downloadCallback);
|
|
}
|
|
}
|
|
|
|
// Update the internet based information and update screen
|
|
void updateData() {
|
|
tft.fillScreen(ILI9341_BLACK);
|
|
tft.setFont(&ArialRoundedMTBold_14);
|
|
drawProgress(20, "Updating time...");
|
|
timeClient.updateTime();
|
|
drawProgress(50, "Updating conditions...");
|
|
wunderground.updateConditions(WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY);
|
|
drawProgress(70, "Updating forecasts...");
|
|
wunderground.updateForecast(WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY);
|
|
drawProgress(90, "Updating astronomy...");
|
|
wunderground.updateAstronomy(WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY);
|
|
//lastUpdate = timeClient.getFormattedTime();
|
|
//readyForWeatherUpdate = false;
|
|
drawProgress(100, "Done...");
|
|
delay(1000);
|
|
tft.fillScreen(ILI9341_BLACK);
|
|
drawTime();
|
|
drawCurrentWeather();
|
|
drawForecast();
|
|
drawAstronomy();
|
|
}
|
|
|
|
// Progress bar helper
|
|
void drawProgress(uint8_t percentage, String text) {
|
|
ui.setTextAlignment(CENTER);
|
|
ui.setTextColor(ILI9341_ORANGE, ILI9341_BLACK);
|
|
tft.fillRect(0, 140, 240, 45, ILI9341_BLACK);
|
|
ui.drawString(120, 160, text);
|
|
ui.drawProgressBar(10, 165, 240 - 20, 15, percentage, ILI9341_WHITE, ILI9341_BLUE);
|
|
}
|
|
|
|
// draws the clock
|
|
void drawTime() {
|
|
ui.setTextAlignment(CENTER);
|
|
ui.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
|
|
tft.setFont(&ArialRoundedMTBold_14);
|
|
String date = wunderground.getDate();
|
|
ui.drawString(120, 20, date);
|
|
|
|
tft.setFont(&ArialRoundedMTBold_36);
|
|
String time = timeClient.getHours() + ":" + timeClient.getMinutes();
|
|
ui.drawString(120, 56, time);
|
|
drawSeparator(65);
|
|
}
|
|
|
|
// draws current weather information
|
|
void drawCurrentWeather() {
|
|
// Weather Icon
|
|
String weatherIcon = getMeteoconIcon(wunderground.getTodayIcon());
|
|
ui.drawBmp(weatherIcon + ".bmp", 0, 55);
|
|
|
|
// Weather Text
|
|
tft.setFont(&ArialRoundedMTBold_14);
|
|
ui.setTextColor(ILI9341_ORANGE, ILI9341_BLACK);
|
|
ui.setTextAlignment(RIGHT);
|
|
ui.drawString(220, 90, wunderground.getWeatherText());
|
|
|
|
tft.setFont(&ArialRoundedMTBold_36);
|
|
ui.setTextColor(ILI9341_ORANGE, ILI9341_BLACK);
|
|
ui.setTextAlignment(RIGHT);
|
|
String degreeSign = "F";
|
|
if (IS_METRIC) {
|
|
degreeSign = "C";
|
|
}
|
|
String temp = wunderground.getCurrentTemp() + degreeSign;
|
|
ui.drawString(220, 125, temp);
|
|
drawSeparator(135);
|
|
|
|
}
|
|
|
|
// draws the three forecast columns
|
|
void drawForecast() {
|
|
drawForecastDetail(10, 165, 0);
|
|
drawForecastDetail(95, 165, 2);
|
|
drawForecastDetail(180, 165, 4);
|
|
drawSeparator(165 + 65 + 10);
|
|
}
|
|
|
|
// helper for the forecast columns
|
|
void drawForecastDetail(uint16_t x, uint16_t y, uint8_t dayIndex) {
|
|
ui.setTextColor(ILI9341_ORANGE, ILI9341_BLACK);
|
|
tft.setFont(&ArialRoundedMTBold_14);
|
|
ui.setTextAlignment(CENTER);
|
|
String day = wunderground.getForecastTitle(dayIndex).substring(0, 3);
|
|
day.toUpperCase();
|
|
ui.drawString(x + 25, y, day);
|
|
|
|
ui.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
|
|
ui.drawString(x + 25, y + 14, wunderground.getForecastLowTemp(dayIndex) + "|" + wunderground.getForecastHighTemp(dayIndex));
|
|
|
|
String weatherIcon = getMeteoconIcon(wunderground.getForecastIcon(dayIndex));
|
|
ui.drawBmp("/mini/" + weatherIcon + ".bmp", x, y + 15);
|
|
|
|
}
|
|
|
|
// draw moonphase and sunrise/set and moonrise/set
|
|
void drawAstronomy() {
|
|
int moonAgeImage = 24 * wunderground.getMoonAge().toInt() / 30.0;
|
|
ui.drawBmp("/moon" + String(moonAgeImage) + ".bmp", 120 - 30, 255);
|
|
|
|
ui.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
|
|
tft.setFont(&ArialRoundedMTBold_14);
|
|
ui.setTextAlignment(LEFT);
|
|
ui.setTextColor(ILI9341_ORANGE, ILI9341_BLACK);
|
|
ui.drawString(20, 270, "Sun");
|
|
ui.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
|
|
ui.drawString(20, 285, wunderground.getSunriseTime());
|
|
ui.drawString(20, 300, wunderground.getSunsetTime());
|
|
|
|
ui.setTextAlignment(RIGHT);
|
|
ui.setTextColor(ILI9341_ORANGE, ILI9341_BLACK);
|
|
ui.drawString(220, 270, "Moon");
|
|
ui.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
|
|
ui.drawString(220, 285, wunderground.getMoonriseTime());
|
|
ui.drawString(220, 300, wunderground.getMoonsetTime());
|
|
|
|
}
|
|
|
|
// Helper function, should be part of the weather station library and should disappear soon
|
|
String getMeteoconIcon(String iconText) {
|
|
if (iconText == "F") return "chanceflurries";
|
|
if (iconText == "Q") return "chancerain";
|
|
if (iconText == "W") return "chancesleet";
|
|
if (iconText == "V") return "chancesnow";
|
|
if (iconText == "S") return "chancetstorms";
|
|
if (iconText == "B") return "clear";
|
|
if (iconText == "Y") return "cloudy";
|
|
if (iconText == "F") return "flurries";
|
|
if (iconText == "M") return "fog";
|
|
if (iconText == "E") return "hazy";
|
|
if (iconText == "Y") return "mostlycloudy";
|
|
if (iconText == "H") return "mostlysunny";
|
|
if (iconText == "H") return "partlycloudy";
|
|
if (iconText == "J") return "partlysunny";
|
|
if (iconText == "W") return "sleet";
|
|
if (iconText == "R") return "rain";
|
|
if (iconText == "W") return "snow";
|
|
if (iconText == "B") return "sunny";
|
|
if (iconText == "0") return "tstorms";
|
|
|
|
|
|
return "unknown";
|
|
}
|
|
|
|
// if you want separators, uncomment the tft-line
|
|
void drawSeparator(uint16_t y) {
|
|
//tft.drawFastHLine(10, y, 240 - 2 * 10, 0x4228);
|
|
}
|
|
|