580 lines
21 KiB
C++
580 lines
21 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 COPYBR_DATUM 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
|
|
|
|
Adapted by Bodmer to use the faster TFT_eSPI library:
|
|
https://github.com/Bodmer/TFT_eSPI
|
|
|
|
Plus:
|
|
Minor changes to text placement and auto-blanking out old text with background colour padding
|
|
Moon phase text added
|
|
Forecast text lines are automatically split onto two lines at a central space (some are long!)
|
|
Time is printed with colons aligned to tidy display
|
|
Min and max forecast temperatures spaced out
|
|
The ` character has been changed to a degree symbol in the 36 point font
|
|
New smart WU jpeg splash startup screen and updated progress messages
|
|
Display does not need to be blanked between updates
|
|
Icons nudged about slightly to add wind direction + speed
|
|
*/
|
|
|
|
#include <Arduino.h>
|
|
|
|
#include <SPI.h>
|
|
#include <TFT_eSPI.h> // Hardware-specific library
|
|
|
|
// 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!!!
|
|
* ***************************/
|
|
|
|
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
|
|
|
|
boolean booted = true;
|
|
|
|
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(250000);
|
|
tft.begin();
|
|
tft.fillScreen(TFT_BLACK);
|
|
|
|
tft.setFreeFont(&ArialRoundedMTBold_14);
|
|
tft.setTextDatum(BC_DATUM);
|
|
tft.setTextColor(TFT_DARKGREY, TFT_BLACK);
|
|
tft.drawString("Original by: blog.squix.org", 120, 240);
|
|
tft.drawString("Adapted by: Bodmer", 120, 260);
|
|
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
|
|
|
|
SPIFFS.begin();
|
|
//listFiles();
|
|
//Uncomment next line if you want to erase SPIFFS and update all internet resources, this takes some time!
|
|
tft.drawString("Formatting SPIFFS, so wait!", 120, 200); SPIFFS.format();
|
|
|
|
if (SPIFFS.exists("/WU.jpg") == true) ui.drawJpeg("/WU.jpg", 0, 10);
|
|
if (SPIFFS.exists("/Earth.jpg") == true) ui.drawJpeg("/Earth.jpg", 0, 320-56); // Image is 56 pixels high
|
|
delay(1000);
|
|
tft.drawString("Connecting to WiFi", 120, 200);
|
|
tft.setTextPadding(240); // Pad next drawString() text to full width to over-write old text
|
|
|
|
//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();
|
|
|
|
// download images from the net. If images already exist don't download
|
|
tft.drawString("Downloading to SPIFFS...", 120, 200);
|
|
tft.drawString(" ", 120, 240); // Clear line
|
|
tft.drawString(" ", 120, 260); // Clear line
|
|
downloadResources();
|
|
//listFiles();
|
|
tft.drawString(" ", 120, 200); // Clear line above using set padding width
|
|
tft.drawString("Fetching weather data...", 120, 220);
|
|
//delay(500);
|
|
|
|
// 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) {
|
|
tft.setTextDatum(BC_DATUM);
|
|
tft.setFreeFont(&ArialRoundedMTBold_14);
|
|
tft.setTextColor(TFT_ORANGE);
|
|
tft.drawString("Wifi Manager", 120, 28);
|
|
tft.drawString("Please connect to AP", 120, 42);
|
|
tft.setTextColor(TFT_WHITE);
|
|
tft.drawString(myWiFiManager->getConfigPortalSSID(), 120, 56);
|
|
tft.setTextColor(TFT_ORANGE);
|
|
tft.drawString("To setup Wifi Configuration", 120, 70);
|
|
}
|
|
|
|
// 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));
|
|
|
|
tft.setTextDatum(BC_DATUM);
|
|
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
|
|
tft.setTextPadding(240);
|
|
|
|
int percentage = 100 * bytesDownloaded / bytesTotal;
|
|
if (percentage == 0) {
|
|
tft.drawString(filename, 120, 220);
|
|
}
|
|
if (percentage % 5 == 0) {
|
|
tft.setTextDatum(TC_DATUM);
|
|
tft.setTextPadding(tft.textWidth(" 888% "));
|
|
tft.drawString(String(percentage) + "%", 120, 245);
|
|
ui.drawProgressBar(10, 225, 240 - 20, 15, percentage, TFT_WHITE, TFT_BLUE);
|
|
}
|
|
|
|
}
|
|
|
|
// Download the bitmaps
|
|
void downloadResources() {
|
|
// tft.fillScreen(TFT_BLACK);
|
|
tft.setFreeFont(&ArialRoundedMTBold_14);
|
|
char id[5];
|
|
|
|
// Download WU graphic jpeg first and display it, then the Earth view
|
|
webResource.downloadFile((String)"http://i.imgur.com/njl1pMj.jpg", (String)"/WU.jpg", _downloadCallback);
|
|
if (SPIFFS.exists("/WU.jpg") == true) ui.drawJpeg("/WU.jpg", 0, 10);
|
|
|
|
webResource.downloadFile((String)"http://i.imgur.com/v4eTLCC.jpg", (String)"/Earth.jpg", _downloadCallback);
|
|
if (SPIFFS.exists("/Earth.jpg") == true) ui.drawJpeg("/Earth.jpg", 0, 320-56);
|
|
|
|
//webResource.downloadFile((String)"http://i.imgur.com/IY57GSv.jpg", (String)"/Horizon.jpg", _downloadCallback);
|
|
//if (SPIFFS.exists("/Horizon.jpg") == true) ui.drawJpeg("/Horizon.jpg", 0, 320-160);
|
|
|
|
//webResource.downloadFile((String)"http://i.imgur.com/jZptbtY.jpg", (String)"/Rainbow.jpg", _downloadCallback);
|
|
//if (SPIFFS.exists("/Rainbow.jpg") == true) ui.drawJpeg("/Rainbow.jpg", 0, 0);
|
|
|
|
for (int i = 0; i < 19; i++) {
|
|
sprintf(id, "%02d", i);
|
|
webResource.downloadFile("http://www.squix.org/blog/wunderground/" + wundergroundIcons[i] + ".bmp", wundergroundIcons[i] + ".bmp", _downloadCallback);
|
|
}
|
|
for (int i = 0; i < 19; i++) {
|
|
sprintf(id, "%02d", i);
|
|
webResource.downloadFile("http://www.squix.org/blog/wunderground/mini/" + wundergroundIcons[i] + ".bmp", "/mini/" + wundergroundIcons[i] + ".bmp", _downloadCallback);
|
|
}
|
|
for (int i = 0; i < 24; i++) {
|
|
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() {
|
|
// booted = true; // Test only
|
|
// booted = false; // Test only
|
|
|
|
if (booted) ui.drawJpeg("/WU.jpg", 0, 10); // May have already drawn this but it does not take long
|
|
else tft.drawCircle(22, 22, 16, TFT_DARKGREY); // Outer ring - optional
|
|
|
|
if (booted) drawProgress(20, "Updating time...");
|
|
else fillSegment(22, 22, 0, (int) (20 * 3.6), 16, TFT_NAVY);
|
|
|
|
timeClient.updateTime();
|
|
if (booted) drawProgress(50, "Updating conditions...");
|
|
else fillSegment(22, 22, 0, (int) (50 * 3.6), 16, TFT_NAVY);
|
|
|
|
wunderground.updateConditions(WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY);
|
|
if (booted) drawProgress(70, "Updating forecasts...");
|
|
else fillSegment(22, 22, 0, (int) (70 * 3.6), 16, TFT_NAVY);
|
|
|
|
wunderground.updateForecast(WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY);
|
|
if (booted) drawProgress(90, "Updating astronomy...");
|
|
else fillSegment(22, 22, 0, (int) (90 * 3.6), 16, TFT_NAVY);
|
|
|
|
wunderground.updateAstronomy(WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY);
|
|
// lastUpdate = timeClient.getFormattedTime();
|
|
// readyForWeatherUpdate = false;
|
|
if (booted) drawProgress(100, "Done...");
|
|
else fillSegment(22, 22, 0, 360, 16, TFT_NAVY);
|
|
|
|
if (booted) delay(2000);
|
|
|
|
if (booted) tft.fillScreen(TFT_BLACK);
|
|
else fillSegment(22, 22, 0, 360, 22, TFT_BLACK);
|
|
|
|
//tft.fillScreen(TFT_CYAN); // For text padding and update graphics over-write checking only
|
|
drawTime();
|
|
drawCurrentWeather();
|
|
drawForecast();
|
|
drawAstronomy();
|
|
|
|
//if (booted) screenshotToConsole(); // No supporting function in this sketch, documentation support only!
|
|
booted = false;
|
|
}
|
|
|
|
// Progress bar helper
|
|
void drawProgress(uint8_t percentage, String text) {
|
|
tft.setFreeFont(&ArialRoundedMTBold_14);
|
|
|
|
tft.setTextDatum(BC_DATUM);
|
|
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
|
|
tft.setTextPadding(240);
|
|
tft.drawString(text, 120, 220);
|
|
|
|
ui.drawProgressBar(10, 225, 240 - 20, 15, percentage, TFT_WHITE, TFT_BLUE);
|
|
|
|
tft.setTextPadding(0);
|
|
}
|
|
|
|
// draws the clock
|
|
void drawTime() {
|
|
tft.setFreeFont(&ArialRoundedMTBold_14);
|
|
|
|
String date = wunderground.getDate();
|
|
|
|
tft.setTextDatum(BC_DATUM);
|
|
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
|
tft.setTextPadding(tft.textWidth(" Ddd, 44 Mmm 4444 ")); // String width + margin
|
|
tft.drawString(date, 120, 14);
|
|
|
|
tft.setFreeFont(&ArialRoundedMTBold_36);
|
|
|
|
String timeNow = timeClient.getHours() + ":" + timeClient.getMinutes();
|
|
|
|
tft.setTextDatum(BC_DATUM);
|
|
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
|
|
tft.setTextPadding(tft.textWidth(" 44:44 ")); // String width + margin
|
|
tft.drawString(timeNow, 120, 50);
|
|
|
|
drawSeparator(52);
|
|
|
|
tft.setTextPadding(0);
|
|
}
|
|
|
|
// draws current weather information
|
|
void drawCurrentWeather() {
|
|
// Weather Icon
|
|
String weatherIcon = getMeteoconIcon(wunderground.getTodayIcon());
|
|
//uint32_t dt = millis();
|
|
ui.drawBmp(weatherIcon + ".bmp", 0, 59);
|
|
//Serial.print("Icon draw time = "); Serial.println(millis()-dt);
|
|
|
|
// Weather Text
|
|
|
|
String weatherText = wunderground.getWeatherText();
|
|
//weatherText = "Heavy Thunderstorms with Small Hail"; // Test line splitting with longest(?) string
|
|
|
|
tft.setFreeFont(&ArialRoundedMTBold_14);
|
|
|
|
tft.setTextDatum(BR_DATUM);
|
|
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
|
|
|
|
int splitPoint = 0;
|
|
int xpos = 230;
|
|
splitPoint = splitIndex(weatherText);
|
|
if (splitPoint > 16) xpos = 235;
|
|
|
|
tft.setTextPadding(tft.textWidth("Heavy Thunderstorms")); // Max anticipated string width
|
|
if (splitPoint) tft.drawString(weatherText.substring(0, splitPoint), xpos, 72);
|
|
tft.setTextPadding(tft.textWidth(" with Small Hail")); // Max anticipated string width + margin
|
|
tft.drawString(weatherText.substring(splitPoint), xpos, 87);
|
|
|
|
tft.setFreeFont(&ArialRoundedMTBold_36);
|
|
|
|
tft.setTextDatum(TR_DATUM);
|
|
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
|
|
|
|
// Font ASCII code 96 (0x60) modified to make "`" a degree symbol
|
|
tft.setTextPadding(tft.textWidth("-88`")); // Max width of vales
|
|
|
|
weatherText = wunderground.getCurrentTemp();
|
|
if (weatherText.indexOf(".")) weatherText = weatherText.substring(0, weatherText.indexOf(".")); // Make it integer temperature
|
|
if (weatherText == "") weatherText = "?"; // Handle null return
|
|
tft.drawString(weatherText + "`", 221, 100);
|
|
|
|
tft.setFreeFont(&ArialRoundedMTBold_14);
|
|
|
|
tft.setTextDatum(TL_DATUM);
|
|
tft.setTextPadding(0);
|
|
if (IS_METRIC) tft.drawString("C ", 221, 100);
|
|
else tft.drawString("F ", 221, 100);
|
|
|
|
weatherText = wunderground.getWindDir() + " ";
|
|
weatherText += String((int)(wunderground.getWindSpeed().toInt() * WIND_SPEED_SCALING)) + WIND_SPEED_UNITS;
|
|
|
|
tft.setTextPadding(tft.textWidth("Variable 888 mph ")); // Max string length?
|
|
tft.drawString(weatherText, 114, 136);
|
|
|
|
weatherText = wunderground.getWindDir();
|
|
|
|
int windAngle = 0;
|
|
String compassCardinal = "";
|
|
switch (weatherText.length()) {
|
|
case 1:
|
|
compassCardinal = "N E S W "; // Not used, see default below
|
|
windAngle = 90 * compassCardinal.indexOf(weatherText) / 2;
|
|
break;
|
|
case 2:
|
|
compassCardinal = "NE SE SW NW";
|
|
windAngle = 45 + 90 * compassCardinal.indexOf(weatherText) / 3;
|
|
break;
|
|
case 3:
|
|
compassCardinal = "NNE ENE ESE SSE SSW WSW WNW NNW";
|
|
windAngle = 22 + 45 * compassCardinal.indexOf(weatherText) / 4; // 22 should be 22.5 but accuracy is not needed!
|
|
break;
|
|
default:
|
|
if (weatherText == "Variable") windAngle = -1;
|
|
else {
|
|
// v23456v23456v23456v23456 character ruler
|
|
compassCardinal = "North East South West"; // Possible strings
|
|
windAngle = 90 * compassCardinal.indexOf(weatherText) / 6;
|
|
}
|
|
break;
|
|
}
|
|
|
|
tft.fillCircle(128, 110, 23, TFT_BLACK); // Erase old plot, radius + 1 to delete stray pixels
|
|
tft.drawCircle(128, 110, 22, TFT_DARKGREY); // Outer ring - optional
|
|
if ( windAngle >= 0 ) fillSegment(128, 110, windAngle - 15, 30, 22, TFT_GREEN); // Might replace this with a bigger rotating arrow
|
|
tft.drawCircle(128, 110, 6, TFT_RED);
|
|
|
|
drawSeparator(153);
|
|
|
|
tft.setTextPadding(0); // Reset padding width to none
|
|
}
|
|
|
|
// draws the three forecast columns
|
|
void drawForecast() {
|
|
drawForecastDetail(10, 171, 0);
|
|
drawForecastDetail(95, 171, 2);
|
|
drawForecastDetail(180, 171, 4);
|
|
drawSeparator(171 + 69);
|
|
}
|
|
|
|
// helper for the forecast columns
|
|
void drawForecastDetail(uint16_t x, uint16_t y, uint8_t dayIndex) {
|
|
tft.setFreeFont(&ArialRoundedMTBold_14);
|
|
|
|
String day = wunderground.getForecastTitle(dayIndex).substring(0, 3);
|
|
day.toUpperCase();
|
|
|
|
tft.setTextDatum(BC_DATUM);
|
|
|
|
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
|
|
tft.setTextPadding(tft.textWidth("WWW"));
|
|
tft.drawString(day, x + 25, y);
|
|
|
|
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
|
tft.setTextPadding(tft.textWidth("-88 -88"));
|
|
tft.drawString(wunderground.getForecastHighTemp(dayIndex) + " " + wunderground.getForecastLowTemp(dayIndex), x + 25, y + 14);
|
|
|
|
String weatherIcon = getMeteoconIcon(wunderground.getForecastIcon(dayIndex));
|
|
ui.drawBmp("/mini/" + weatherIcon + ".bmp", x, y + 15);
|
|
|
|
tft.setTextPadding(0); // Reset padding width to none
|
|
}
|
|
|
|
// draw moonphase and sunrise/set and moonrise/set
|
|
void drawAstronomy() {
|
|
tft.setFreeFont(&ArialRoundedMTBold_14);
|
|
|
|
tft.setTextDatum(BC_DATUM);
|
|
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
|
|
tft.setTextPadding(tft.textWidth(" Waxing Crescent "));
|
|
tft.drawString(wunderground.getMoonPhase(), 120, 260 - 2);
|
|
|
|
int moonAgeImage = 24 * wunderground.getMoonAge().toInt() / 30.0;
|
|
ui.drawBmp("/moon" + String(moonAgeImage) + ".bmp", 120 - 30, 260);
|
|
|
|
tft.setTextDatum(BC_DATUM);
|
|
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
|
|
tft.setTextPadding(0); // Reset padding width to none
|
|
tft.drawString("Sun", 40, 280);
|
|
|
|
tft.setTextDatum(BR_DATUM);
|
|
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
|
tft.setTextPadding(tft.textWidth(" 88:88 "));
|
|
int dt = rightOffset(wunderground.getSunriseTime(), ":"); // Draw relative to colon to them aligned
|
|
tft.drawString(wunderground.getSunriseTime(), 40 + dt, 300);
|
|
|
|
dt = rightOffset(wunderground.getSunsetTime(), ":");
|
|
tft.drawString(wunderground.getSunsetTime(), 40 + dt, 315);
|
|
|
|
tft.setTextDatum(BC_DATUM);
|
|
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
|
|
tft.setTextPadding(0); // Reset padding width to none
|
|
tft.drawString("Moon", 200, 280);
|
|
|
|
tft.setTextDatum(BR_DATUM);
|
|
tft.setTextColor(TFT_WHITE, TFT_BLACK);
|
|
tft.setTextPadding(tft.textWidth(" 88:88 "));
|
|
dt = rightOffset(wunderground.getMoonriseTime(), ":"); // Draw relative to colon to them aligned
|
|
tft.drawString(wunderground.getMoonriseTime(), 200 + dt, 300);
|
|
|
|
dt = rightOffset(wunderground.getMoonsetTime(), ":");
|
|
tft.drawString(wunderground.getMoonsetTime(), 200 + dt, 315);
|
|
|
|
tft.setTextPadding(0); // Reset padding width to none
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
// determine the "space" split point in a long string
|
|
int splitIndex(String text)
|
|
{
|
|
int index = 0;
|
|
while ( (text.indexOf(' ', index) >= 0) && ( index <= text.length() / 2 ) ) {
|
|
index = text.indexOf(' ', index) + 1;
|
|
}
|
|
if (index) index--;
|
|
return index;
|
|
}
|
|
|
|
// Calculate coord delta from start of text String to start of sub String contained within that text
|
|
// Can be used to vertically right align text so for example a colon ":" in the time value is always
|
|
// plotted at same point on the screen irrespective of different proportional character widths,
|
|
// could also be used to align decimal points for neat formatting
|
|
int rightOffset(String text, String sub)
|
|
{
|
|
int index = text.indexOf(sub);
|
|
return tft.textWidth(text.substring(index));
|
|
}
|
|
|
|
// Calculate coord delta from start of text String to start of sub String contained within that text
|
|
// Can be used to vertically left align text so for example a colon ":" in the time value is always
|
|
// plotted at same point on the screen irrespective of different proportional character widths,
|
|
// could also be used to align decimal points for neat formatting
|
|
int leftOffset(String text, String sub)
|
|
{
|
|
int index = text.indexOf(sub);
|
|
return tft.textWidth(text.substring(0, index));
|
|
}
|
|
|
|
// Draw a segment of a circle, centred on x,y with defined start_angle and subtended sub_angle
|
|
// Angles are defined in a clockwise direction with 0 at top
|
|
// Segment has radius r and it is plotted in defined colour
|
|
// Can be used for pie charts etc, in this sketch it is used for wind direction
|
|
#define DEG2RAD 0.0174532925 // Degrees to Radians conversion factor
|
|
#define INC 2 // Minimum segment subtended angle and plotting angle increment (in degrees)
|
|
void fillSegment(int x, int y, int start_angle, int sub_angle, int r, unsigned int colour)
|
|
{
|
|
// Calculate first pair of coordinates for segment start
|
|
float sx = cos((start_angle - 90) * DEG2RAD);
|
|
float sy = sin((start_angle - 90) * DEG2RAD);
|
|
uint16_t x1 = sx * r + x;
|
|
uint16_t y1 = sy * r + y;
|
|
|
|
// Draw colour blocks every INC degrees
|
|
for (int i = start_angle; i < start_angle + sub_angle; i += INC) {
|
|
|
|
// Calculate pair of coordinates for segment end
|
|
int x2 = cos((i + 1 - 90) * DEG2RAD) * r + x;
|
|
int y2 = sin((i + 1 - 90) * DEG2RAD) * r + y;
|
|
|
|
tft.fillTriangle(x1, y1, x2, y2, x, y, colour);
|
|
|
|
// Copy segment end to sgement start for next segment
|
|
x1 = x2;
|
|
y1 = y2;
|
|
}
|
|
}
|