Added touch screen features, calibration and a few more screens

This commit is contained in:
Daniel Eichhorn
2017-07-09 18:21:47 +02:00
parent 3f51a3d6ff
commit 836109eecc
5 changed files with 538 additions and 34 deletions

View File

@@ -21,6 +21,8 @@ See more at https://blog.squix.org
#include <Arduino.h>
#include <SPI.h>
#include <ESP8266WiFi.h>
#include <XPT2046_Touchscreen.h>
#include "TouchController.h"
/***
* Install the following libraries through Arduino Library Manager
@@ -34,6 +36,7 @@ See more at https://blog.squix.org
#include <WundergroundConditions.h>
#include <WundergroundForecast.h>
#include <WundergroundAstronomy.h>
#include <WundergroundAlerts.h>
#include <MiniGrafx.h>
#include <Carousel.h>
#include <ILI9341_SPI.h>
@@ -54,6 +57,7 @@ See more at https://blog.squix.org
#define MINI_BLUE 3
#define MAX_FORECASTS 12
#define MAX_ALERTS 1
// defines the colors usable in the paletted 16 color frame buffer
uint16_t palette[] = {ILI9341_BLACK, // 0
@@ -66,6 +70,8 @@ int SCREEN_HEIGHT = 320;
// Limited to 4 colors due to memory constraints
int BITS_PER_PIXEL = 2; // 2^2 = 4 colors
ADC_MODE(ADC_VCC);
// HOSTNAME for OTA update
#define HOSTNAME "ESP8266-OTA-"
@@ -74,9 +80,13 @@ ILI9341_SPI tft = ILI9341_SPI(TFT_CS, TFT_DC);
MiniGrafx gfx = MiniGrafx(&tft, BITS_PER_PIXEL, palette);
Carousel carousel(&gfx, 0, 0, 240, 100);
XPT2046_Touchscreen ts(TOUCH_CS, TOUCH_IRQ);
TouchController touchController(&ts);
WGConditions conditions;
WGForecast forecasts[MAX_FORECASTS];
WGAstronomy astronomy;
WGAlert alerts[MAX_ALERTS];
// Setup simpleDSTadjust Library rules
simpleDSTadjust dstAdjusted(StartRule, EndRule);
@@ -88,18 +98,27 @@ void drawCurrentWeather();
void drawForecast();
void drawForecastDetail(uint16_t x, uint16_t y, uint8_t dayIndex);
void drawAstronomy();
void drawAlert();
void drawCurrentWeatherDetail();
void drawLabelValue(uint8_t line, String label, String value);
void drawForecastTable(uint8_t start);
void drawAbout();
void drawSeparator(uint16_t y);
const char* getMeteoconIconFromProgmem(String iconText);
const char* getMiniMeteoconIconFromProgmem(String iconText);
void drawForecast1(MiniGrafx *display, CarouselState* state, int16_t x, int16_t y);
void drawForecast2(MiniGrafx *display, CarouselState* state, int16_t x, int16_t y);
FrameCallback frames[] = { drawForecast1, drawForecast2 };
void calibrationCallback(int16_t x, int16_t y);
CalibrationCallback calibration = &calibrationCallback;
int frameCount = 2;
// how many different screens do we have?
int screenCount = 5;
long lastDownloadUpdate = millis();
void updateCalendar();
String moonAgeImage = "";
uint16_t screen = 0;
void setup() {
Serial.begin(115200);
@@ -111,6 +130,29 @@ void setup() {
digitalWrite(TFT_LED, HIGH);
gfx.init();
gfx.fillBuffer(MINI_BLACK);
gfx.commit();
ts.begin();
SPIFFS.begin();
//SPIFFS.remove("/calibration.txt");
boolean isCalibrationAvailable = touchController.loadCalibration();
if (!isCalibrationAvailable) {
Serial.println("Calibration not available");
touchController.startCalibration(&calibration);
while (!touchController.isCalibrationFinished()) {
gfx.fillBuffer(0);
gfx.setColor(MINI_YELLOW);
gfx.setTextAlignment(TEXT_ALIGN_CENTER);
gfx.drawString(120, 160, "Please calibrate\ntouch screen by\ntouch point");
touchController.continueCalibration();
gfx.commit();
yield();
}
touchController.saveCalibration();
}
gfx.fillBuffer(MINI_BLACK);
gfx.setFont(ArialRoundedMTBold_14);
gfx.setColor(MINI_YELLOW);
@@ -135,21 +177,41 @@ void setup() {
long lastDrew = 0;
void loop() {
gfx.fillBuffer(MINI_BLACK);
drawTime();
drawCurrentWeather();
int remainingTimeBudget = carousel.update();
if (remainingTimeBudget > 0) {
// You can do some work here
// Don't do stuff if you are below your
// time budget.
delay(remainingTimeBudget);
if (touchController.isTouched(500)) {
TS_Point p = touchController.getPoint();
if (p.y < 80) {
IS_STYLE_12HR = !IS_STYLE_12HR;
} else {
screen = (screen + 1) % screenCount;
}
}
gfx.fillBuffer(MINI_BLACK);
if (screen == 0) {
drawTime();
int remainingTimeBudget = carousel.update();
if (remainingTimeBudget > 0) {
// You can do some work here
// Don't do stuff if you are below your
// time budget.
delay(remainingTimeBudget);
}
drawCurrentWeather();
drawAstronomy();
} else if (screen == 1) {
drawCurrentWeatherDetail();
} else if (screen == 2) {
drawForecastTable(0);
} else if (screen == 3) {
drawForecastTable(6);
} else if (screen == 4) {
drawAbout();
}
drawAstronomy();
gfx.commit();
// Check if we should update weather information
if (millis() - lastDownloadUpdate > 1000 * UPDATE_INTERVAL_SECS) {
updateData();
@@ -177,22 +239,33 @@ void updateData() {
forecastClient->updateForecast(forecasts, MAX_FORECASTS, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY);
delete forecastClient;
forecastClient = nullptr;
drawProgress(80, "Updating astronomy...");
WundergroundAstronomy *astronomyClient = new WundergroundAstronomy(STYLE_12HR);
WundergroundAstronomy *astronomyClient = new WundergroundAstronomy(IS_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(90, "Updating alerts...");
WundergroundAlerts *alertClient = new WundergroundAlerts();
alertClient->updateAlerts(alerts, MAX_ALERTS, WUNDERGRROUND_API_KEY, WUNDERGRROUND_LANGUAGE, WUNDERGROUND_COUNTRY, WUNDERGROUND_CITY);
delete alertClient;
alertClient = nullptr;
delay(1000);
}
// Progress bar helper
void drawProgress(uint8_t percentage, String text) {
gfx.fillBuffer(MINI_BLACK);
gfx.drawPalettedBitmapFromPgm(23, 30, SquixLogo);
gfx.setFont(ArialRoundedMTBold_14);
gfx.setTextAlignment(TEXT_ALIGN_CENTER);
gfx.setColor(MINI_WHITE);
gfx.drawString(120, 80, "https://blog.squix.org");
gfx.setColor(MINI_YELLOW);
gfx.drawString(120, 146, text);
@@ -207,15 +280,6 @@ void drawProgress(uint8_t percentage, String text) {
// draws the clock
void drawTime() {
/*gfx.setTextAlignment(TEXT_ALIGN_CENTER);
gfx.setColor(MINI_WHITE);
gfx.setFont(ArialRoundedMTBold_14);
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);*/
char *dstAbbrev;
char time_str[11];
time_t now = dstAdjusted.time(&dstAbbrev);
@@ -223,13 +287,14 @@ void drawTime() {
gfx.setTextAlignment(TEXT_ALIGN_CENTER);
gfx.setFont(ArialRoundedMTBold_14);
gfx.setColor(MINI_WHITE);
String date = ctime(&now);
date = date.substring(0,11) + String(1900 + timeinfo->tm_year);
gfx.drawString(120, 6, date);
gfx.setFont(ArialRoundedMTBold_36);
if (STYLE_12HR) {
if (IS_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);
@@ -241,7 +306,7 @@ void drawTime() {
gfx.setTextAlignment(TEXT_ALIGN_LEFT);
gfx.setFont(ArialMT_Plain_10);
gfx.setColor(MINI_BLUE);
if (STYLE_12HR) {
if (IS_STYLE_12HR) {
sprintf(time_str, "%s\n%s", dstAbbrev, timeinfo->tm_hour>=12?"PM":"AM");
gfx.drawString(195, 27, time_str);
} else {
@@ -255,10 +320,11 @@ void drawCurrentWeather() {
gfx.setTransparentColor(MINI_BLACK);
gfx.drawPalettedBitmapFromPgm(0, 55, getMeteoconIconFromProgmem(conditions.weatherIcon));
// Weather Text
gfx.setFont(ArialRoundedMTBold_14);
gfx.setColor(MINI_YELLOW);
gfx.setColor(MINI_BLUE);
gfx.setTextAlignment(TEXT_ALIGN_RIGHT);
gfx.drawString(220, 76, conditions.weatherText);
gfx.drawString(220, 65, DISPLAYED_CITY_NAME);
gfx.setFont(ArialRoundedMTBold_36);
gfx.setColor(MINI_WHITE);
@@ -268,7 +334,12 @@ void drawCurrentWeather() {
degreeSign = "°C";
}
String temp = conditions.currentTemp + degreeSign;
gfx.drawString(220, 89, temp);
gfx.drawString(220, 78, temp);
gfx.setFont(ArialRoundedMTBold_14);
gfx.setColor(MINI_YELLOW);
gfx.setTextAlignment(TEXT_ALIGN_RIGHT);
gfx.drawString(220, 118, conditions.weatherText);
}
@@ -331,6 +402,121 @@ void drawAstronomy() {
}
void drawAlert() {
}
void drawCurrentWeatherDetail() {
gfx.setFont(ArialRoundedMTBold_14);
gfx.setTextAlignment(TEXT_ALIGN_CENTER);
gfx.setColor(MINI_WHITE);
gfx.drawString(120, 2, "Current Conditions");
//gfx.setTransparentColor(MINI_BLACK);
//gfx.drawPalettedBitmapFromPgm(0, 20, getMeteoconIconFromProgmem(conditions.weatherIcon));
String degreeSign = "°F";
if (IS_METRIC) {
degreeSign = "°C";
}
// String weatherIcon;
// String weatherText;
drawLabelValue(0, "Temperature:", conditions.currentTemp + degreeSign);
drawLabelValue(1, "Feels Like:", conditions.feelslike + degreeSign);
drawLabelValue(2, "Dew Point:", conditions.dewPoint + degreeSign);
drawLabelValue(3, "Wind Speed:", conditions.windSpeed);
drawLabelValue(4, "Wind Dir:", conditions.windDir);
drawLabelValue(5, "Humidity:", conditions.humidity);
drawLabelValue(6, "Pressure:", conditions.pressure);
drawLabelValue(7, "Precipitation:", conditions.precipitationToday);
drawLabelValue(8, "UV:", conditions.UV);
gfx.setTextAlignment(TEXT_ALIGN_LEFT);
gfx.setColor(MINI_YELLOW);
gfx.drawString(15, 185, "Description: ");
gfx.setColor(MINI_WHITE);
gfx.drawStringMaxWidth(15, 200, 240 - 2 * 15, forecasts[0].forecastText);
}
void drawLabelValue(uint8_t line, String label, String value) {
const uint8_t labelX = 15;
const uint8_t valueX = 150;
gfx.setTextAlignment(TEXT_ALIGN_LEFT);
gfx.setColor(MINI_YELLOW);
gfx.drawString(labelX, 30 + line * 15, label);
gfx.setColor(MINI_WHITE);
gfx.drawString(valueX, 30 + line * 15, value);
}
void drawForecastTable(uint8_t start) {
gfx.setFont(ArialRoundedMTBold_14);
gfx.setTextAlignment(TEXT_ALIGN_CENTER);
gfx.setColor(MINI_WHITE);
gfx.drawString(120, 2, "Forecasts");
uint16_t y = 0;
String degreeSign = "°F";
if (IS_METRIC) {
degreeSign = "°C";
}
for (uint8_t i = start; i < start + 6; i++) {
gfx.setTextAlignment(TEXT_ALIGN_LEFT);
y = 30 + (i - start) * 45;
if (y > 320) {
break;
}
gfx.drawPalettedBitmapFromPgm(0, y, getMiniMeteoconIconFromProgmem(forecasts[i].forecastIcon));
gfx.setColor(MINI_YELLOW);
gfx.setFont(ArialRoundedMTBold_14);
gfx.drawString(50, y, forecasts[i].forecastTitle);
gfx.setColor(MINI_WHITE);
gfx.drawString(50, y + 15, getShortText(forecasts[i].forecastIcon));
gfx.setColor(MINI_WHITE);
gfx.setTextAlignment(TEXT_ALIGN_RIGHT);
String temp = "";
if (i % 2 == 0) {
temp = forecasts[i].forecastHighTemp;
} else {
temp = forecasts[i - 1].forecastLowTemp;
}
gfx.drawString(235, y, temp + degreeSign);
/*gfx.setColor(MINI_WHITE);
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(235, y + 15, forecasts[i].PoP + "%");
}
}
void drawAbout() {
gfx.fillBuffer(MINI_BLACK);
gfx.drawPalettedBitmapFromPgm(23, 30, SquixLogo);
gfx.setFont(ArialRoundedMTBold_14);
gfx.setTextAlignment(TEXT_ALIGN_CENTER);
gfx.setColor(MINI_WHITE);
gfx.drawString(120, 80, "https://blog.squix.org");
gfx.setFont(ArialRoundedMTBold_14);
gfx.setTextAlignment(TEXT_ALIGN_CENTER);
gfx.setColor(MINI_WHITE);
gfx.drawString(120, 115, "About");
drawLabelValue(8, "Heap Mem:", String(ESP.getFreeHeap() / 1024)+"kb");
drawLabelValue(9, "Flash Mem:", String(ESP.getFlashChipRealSize() / 1024 / 1024) + "MB");
drawLabelValue(10, "WiFi Strength:", String(WiFi.RSSI()) + "dB");
drawLabelValue(11, "Chip ID:", String(ESP.getChipId()));
drawLabelValue(12, "VCC: ", String(ESP.getVcc() / 1024.0) +"V");
drawLabelValue(13, "CPU Freq.: ", String(ESP.getCpuFreqMHz()) + "MHz");
}
void calibrationCallback(int16_t x, int16_t y) {
gfx.setColor(1);
gfx.fillCircle(x, y, 10);
}
// Helper function, should be part of the weather station library and should disappear soon
const char* getMeteoconIconFromProgmem(String iconText) {
@@ -339,7 +525,7 @@ const char* getMeteoconIconFromProgmem(String iconText) {
if (iconText == "chancesleet") return chancesleet;
if (iconText == "chancesnow") return chancesnow;
if (iconText == "chancetstorms") return chancestorms;
if (iconText == "cloudy") return clear;
if (iconText == "clear") return clear;
if (iconText == "cloudy") return cloudy;
if (iconText == "flurries") return flurries;
if (iconText == "fog") return fog;
@@ -381,5 +567,31 @@ const char* getMiniMeteoconIconFromProgmem(String iconText) {
return miniunknown;
}
// Helper function, should be part of the weather station library and should disappear soon
const String getShortText(String iconText) {
if (iconText == "chanceflurries") return "Chance of Flurries";
if (iconText == "chancerain") return "Chance of Rain";
if (iconText == "chancesleet") return "Chance of Sleet";
if (iconText == "chancesnow") return "Chance of Snow";
if (iconText == "chancetstorms") return "Chance of Storms";
if (iconText == "clear") return "Clear";
if (iconText == "cloudy") return "Cloudy";
if (iconText == "flurries") return "Flurries";
if (iconText == "fog") return "Fog";
if (iconText == "hazy") return "Hazy";
if (iconText == "mostlycloudy") return "Mostly Cloudy";
if (iconText == "mostlysunny") return "Mostly Sunny";
if (iconText == "partlycloudy") return "Partly Couldy";
if (iconText == "partlysunny") return "Partly Sunny";
if (iconText == "sleet") return "Sleet";
if (iconText == "rain") return "Rain";
if (iconText == "snow") return "Snow";
if (iconText == "sunny") return "Sunny";
if (iconText == "tstorms") return "Storms";
return "-";
}