Dále jsem chtěl vedle hodin wifi rssi jako graf, nemám na to zkoušel jsem do kódu všechno možné a zase nic.
Vzhled stanice jsem trochu pozměnil vypadá to dobře, zatím dík za každou radu.
Tady je celý kod:
Kód: Vybrat vše
#include <SD.h>
// Example from OpenWeather library: https://github.com/Bodmer/OpenWeather
// Adapted by Bodmer to use the TFT_eSPI library: https://github.com/Bodmer/TFT_eSPI
// This sketch is compatible with the ESP8266 and ESP32
// >>> IMPORTANT <<<
// Modify setup in All_Settings.h tab to configure your location etc
// >>> EVEN MORE IMPORTANT TO PREVENT CRASHES <<<
//>>>>>> For ESP8266 set SPIFFS to at least 2Mbytes before uploading files <<<<<<
// ESP8266/ESP32 pin connections to the TFT are defined in the TFT_eSPI library.
// Original by Daniel Eichhorn, see license at end of file.
//#define SERIAL_MESSAGES // For serial output weather reports
//#define SCREEN_SERVER // For dumping screen shots from TFT
//#define RANDOM_LOCATION // Test only, selects random weather location every refresh
//#define FORMAT_SPIFFS // Wipe SPIFFS and all files!
// This sketch uses font files created from the Noto family of fonts as bitmaps
// generated from these fonts may be freely distributed:
// https://www.google.com/get/noto/
// A processing sketch to create new fonts can be found in the Tools folder of TFT_eSPI
// https://github.com/Bodmer/TFT_eSPI/tree/master/Tools/Create_Smooth_Font/Create_font
// New fonts can be generated to include language specific characters. The Noto family
// of fonts has an extensive character set coverage.
// Json streaming parser (do not use IDE library manager version) to use is here:
// https://github.com/Bodmer/JSON_Decoder
#define AA_FONT_SMALL "fonts/NotoSansBold15" // 15 point sans serif bold
#define AA_FONT_LARGE "fonts/NotoSansBold36" // 36 point sans serif bold
/***************************************************************************************
** Načtěte knihovny a nastavení
***************************************************************************************/
#include <Arduino.h>
#include <SPI.h>
#include <TFT_eSPI.h> // https://github.com/Bodmer/TFT_eSPI
// Additional functions
#include "GfxUi.h" // Attached to this sketch
#include "SPIFFS_Support.h" // Attached to this sketch
#ifdef ESP8266
#include <ESP8266WiFi.h>
#else
#include <WiFi.h>
#endif
// check All_Settings.h for adapting to your needs
#include "All_Settings.h"
#include <JSON_Decoder.h> // https://github.com/Bodmer/JSON_Decoder
#include <OpenWeather.h> // Latest here: https://github.com/Bodmer/OpenWeather
#include "NTP_Time.h" // Attached to this sketch, see that tab for library needs
/***************************************************************************************
** Definujte globály a instance třídy
***************************************************************************************/
TFT_eSPI tft = TFT_eSPI(); // Invoke custom library
OW_Weather ow; // Weather forecast library instance
OW_current *current; // Pointers to structs that temporarily holds weather data
OW_hourly *hourly; // Not used
OW_daily *daily;
boolean booted = true;
GfxUi ui = GfxUi(&tft); // Jpeg and bmpDraw functions TODO: pull outside of a class
long lastDownloadUpdate = millis();
/***************************************************************************************
** Deklarovat prototypy
***************************************************************************************/
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);
const char* getMeteoconIcon(uint16_t id, bool today);
void drawAstronomy();
void drawSeparator(uint16_t y);
void fillSegment(int x, int y, int start_angle, int sub_angle, int r, unsigned int colour);
String strDate(time_t unixTime);
String strTime(time_t unixTime);
void printWeather(void);
int leftOffset(String text, String sub);
int rightOffset(String text, String sub);
int splitIndex(String text);
/***************************************************************************************
** Nastavení
***************************************************************************************/
void setup() {
Serial.begin(250000);
tft.begin();
tft.fillScreen(TFT_BLACK);
SPIFFS.begin();
listFiles();
// Enable if you want to erase SPIFFS, this takes some time!
// then disable and reload sketch to avoid reformatting on every boot!
#ifdef FORMAT_SPIFFS
tft.setTextDatum(BC_DATUM); // Bottom Centre datum
tft.drawString("Formatting SPIFFS, so wait!", 120, 195); SPIFFS.format();
#endif
// Draw splash screen
if (SPIFFS.exists("/splash/OpenWeather.jpg") == true) ui.drawJpeg("/splash/OpenWeather.jpg", 0, 40);
delay(2000);
// Clear bottom section of screen
tft.fillRect(0, 206, 240, 320 - 206, TFT_BLACK);
tft.loadFont(AA_FONT_SMALL);
tft.setTextDatum(BC_DATUM); // Bottom Centre datum
tft.setTextColor(TFT_LIGHTGREY, TFT_BLACK);
tft.drawString("Meteorologicka stanice", 120, 260);
tft.drawString("Vojta", 120, 280);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
delay(2000);
tft.fillRect(0, 206, 240, 320 - 206, TFT_BLACK);
tft.drawString("Pripojeni k WiFi ...", 120, 240);
tft.setTextPadding(240); // Pad next drawString() text to full width to over-write old text
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
}
Serial.println();
tft.setTextDatum(BC_DATUM);
tft.setTextPadding(240); // Pad next drawString() text to full width to over-write old text
tft.drawString(" ", 120, 220); // Clear line above using set padding width
tft.drawString("Nacitani udaju o pocasi...", 120, 240);
// Fetch the time
udp.begin(localPort);
syncTime();
tft.unloadFont();
ow.partialDataSet(true); // Collect a subset of the data available
}
/***************************************************************************************
** Loop
***************************************************************************************/
void loop() {
// Check if we should update weather information
if (booted || (millis() - lastDownloadUpdate > 1000UL * UPDATE_INTERVAL_SECS))
{
updateData();
lastDownloadUpdate = millis();
}
// If minute has changed then request new time from NTP server
if (booted || minute() != lastMinute)
{
// Update displayed time first as we may have to wait for a response
drawTime();
lastMinute = minute();
// Request and synchronise the local clock
syncTime();
#ifdef SCREEN_SERVER
screenServer();
#endif
}
booted = false;
}
/***************************************************************************************
** Načtěte údaje o počasí a aktualizujte obrazovku
***************************************************************************************/
// Update the Internet based information and update screen
void updateData() {
// booted = true; // Test only
// booted = false; // Test only
tft.loadFont(AA_FONT_SMALL);
if (booted) drawProgress(20, "Aktualizace casu ...");
else fillSegment(22, 22, 0, (int) (20 * 3.6), 16, TFT_NAVY);
if (booted) drawProgress(50, "Aktualizace podminek...");
else fillSegment(22, 22, 0, (int) (50 * 3.6), 16, TFT_NAVY);
// Create the structures that hold the retrieved weather
current = new OW_current;
daily = new OW_daily;
hourly = new OW_hourly;
#ifdef RANDOM_LOCATION // Randomly choose a place on Earth to test icons etc
String latitude = "";
latitude = (random(180) - 90);
String longitude = "";
longitude = (random(360) - 180);
Serial.print("Lat = "); Serial.print(latitude);
Serial.print(", CZ = "); Serial.println(longitude);
#endif
//On the ESP8266 (only) the library by default uses BearSSL, another option is to use AXTLS
//For problems with ESP8266 stability, use AXTLS by adding a false parameter thus vvvvv
//ow.getForecast(current, hourly, daily, api_key, latitude, longitude, units, language, false);
bool parsed = ow.getForecast(current, hourly, daily, api_key, latitude, longitude, units, language);
if (parsed) Serial.println("Data points received");
else Serial.println("Failed to get data points");
Serial.print("Free heap = "); Serial.println(ESP.getFreeHeap(), DEC);
printWeather(); // For debug, turn on output with #define SERIAL_MESSAGES
if (booted)
{
drawProgress(100, "Hotovo ...");
delay(2000);
tft.fillScreen(TFT_BLACK);
}
else
{
fillSegment(22, 22, 0, 360, 16, TFT_NAVY);
fillSegment(22, 22, 0, 360, 22, TFT_BLACK);
}
if (parsed)
{
drawCurrentWeather();
drawForecast();
drawAstronomy();
tft.unloadFont();
// Update the temperature here so we don't need to keep
// loading and unloading font which takes time
tft.loadFont(AA_FONT_LARGE);
tft.setTextDatum(TR_DATUM);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
// Font ASCII code 0xB0 is a degree symbol, but o used instead in small font
tft.setTextPadding(tft.textWidth(" -88")); // Max width of values
String weatherText = "";
weatherText = (int16_t) current->temp; // Make it integer temperature
tft.drawString(weatherText, 215, 95); // + "°" symbol is big... use o in small font
}
else
{
Serial.println("Failed to get weather");
}
// Delete to free up space
delete current;
delete hourly;
delete daily;
tft.unloadFont();
}
/***************************************************************************************
** Aktualizujte ukazatel průběhu
***************************************************************************************/
void drawProgress(uint8_t percentage, String text) {
tft.setTextDatum(BC_DATUM);
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
tft.setTextPadding(240);
tft.drawString(text, 120, 260);
ui.drawProgressBar(10, 269, 240 - 20, 15, percentage, TFT_WHITE, TFT_BLUE);
tft.setTextPadding(0);
}
/***************************************************************************************
** Nakreslete číslice hodin
***************************************************************************************/
void drawTime() {
tft.loadFont(AA_FONT_LARGE);
// Convert UTC to local time, returns zone code in tz1_Code, e.g "GMT"
time_t local_time = TIMEZONE.toLocal(now(), &tz1_Code);
String timeNow = "";
if (hour(local_time) < 10) timeNow += "0";
timeNow += hour(local_time);
timeNow += ":";
if (minute(local_time) < 10) timeNow += "0";
timeNow += minute(local_time) ;
timeNow += ":";
if (second(local_time) < 10) timeNow += "0";
timeNow += second(local_time) ;
tft.setTextDatum(BC_DATUM);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.setTextPadding(tft.textWidth(" 44:44 ")); // String width + margin
tft.drawString(timeNow, 120, 53);
drawSeparator(51);
tft.setTextPadding(0);
tft.unloadFont();
}
/***************************************************************************************
** Nakreslete aktuální počasí
***************************************************************************************/
void drawCurrentWeather() {
String date = "Havirov " + strDate(current->dt);
String weatherText = "None";
tft.setTextDatum(BC_DATUM);
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
tft.setTextPadding(tft.textWidth(" Updated: Mmm 44 44:44 ")); // String width + margin
tft.drawString(date, 120, 16);
String weatherIcon = "";
String currentSummary = current->main;
currentSummary.toLowerCase();
weatherIcon = getMeteoconIcon(current->id, true);
//uint32_t dt = millis();
ui.drawBmp("/icon/" + weatherIcon + ".bmp", 0, 53);
//Serial.print("Icon draw time = "); Serial.println(millis()-dt);
// Weather Text
if (language == "en")
weatherText = current->main;
else
weatherText = current->description;
tft.setTextDatum(BR_DATUM);
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
int splitPoint = 0;
int xpos = 235;
splitPoint = splitIndex(weatherText);
tft.setTextPadding(xpos - 100); // xpos - icon width
if (splitPoint) tft.drawString(weatherText.substring(0, splitPoint), xpos, 69);
else tft.drawString(" ", xpos, 69);
tft.drawString(weatherText.substring(splitPoint), xpos, 86);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
tft.setTextDatum(TR_DATUM);
tft.setTextPadding(0);
if (units == "metric") tft.drawString("°C", 237, 95);
else tft.drawString("°F", 237, 95);
//V aktualizaci Data () byly přidány velké číslice teploty, aby se sem uložilo vyměnitelné písmo
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
weatherText = (uint16_t)current->wind_speed;
if (units == "metric") weatherText += " m/s";
else weatherText += " mph";
tft.setTextDatum(TC_DATUM);
tft.setTextPadding(tft.textWidth("888 m/s")); // Max string length?
tft.drawString(weatherText, 124, 63);
if (units == "imperial")
{
weatherText = current->pressure * 0.02953;
weatherText += " in";
}
else
{
weatherText = (uint16_t)current->pressure;
weatherText += " hPa";
}
tft.setTextDatum(TR_DATUM);
tft.setTextPadding(tft.textWidth(" 8888hPa")); // Max string length?
tft.drawString(weatherText, 230, 136);
int windAngle = (current->wind_deg + 22.5) / 45;
if (windAngle > 7) windAngle = 0;
String wind[] = {"N", "NE", "E", "SE", "S", "SW", "W", "NW" };
ui.drawBmp("/wind/" + wind[windAngle] + ".bmp", 101, 86);
drawSeparator(153);
tft.setTextDatum(TL_DATUM); // Reset datum to normal
tft.setTextPadding(0); // Reset padding width to none
}
/***************************************************************************************
** Nakreslete 4 sloupce prognózy
***************************************************************************************/
// draws the three forecast columns
void drawForecast() {
int8_t dayIndex = 1;
drawForecastDetail( 8, 171, dayIndex++);
drawForecastDetail( 66, 171, dayIndex++); // was 95
drawForecastDetail(124, 171, dayIndex++); // was 180
drawForecastDetail(182, 171, dayIndex ); // was 180
drawSeparator(171 + 69);
}
/***************************************************************************************
** Nakreslete 1 sloupec prognózy na x, y
***************************************************************************************/
// helper for the forecast columns
void drawForecastDetail(uint16_t x, uint16_t y, uint8_t dayIndex) {
if (dayIndex >= MAX_DAYS) return;
String day = shortDOW[weekday(TIMEZONE.toLocal(daily->dt[dayIndex], &tz1_Code))];
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"));
String highTemp = String(daily->temp_max[dayIndex], 0);
String lowTemp = String(daily->temp_min[dayIndex], 0);
tft.drawString(highTemp + "|" + lowTemp, x + 25, y + 17);
String weatherIcon = getMeteoconIcon(daily->id[dayIndex], false);
ui.drawBmp("/icon50/" + weatherIcon + ".bmp", x, y + 18);
tft.setTextPadding(0); // Reset padding width to none
}
/***************************************************************************************
** Nakreslete východ / západ slunce, měsíc, oblačnost a vlhkost
***************************************************************************************/
void drawAstronomy() {
tft.setTextDatum(BC_DATUM);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextPadding(tft.textWidth(" Last qtr "));
time_t local_time = TIMEZONE.toLocal(current->dt, &tz1_Code);
uint16_t y = year(local_time);
uint8_t m = month(local_time);
uint8_t d = day(local_time);
uint8_t h = hour(local_time);
int ip;
uint8_t icon = moon_phase(y, m, d, h, &ip);
tft.drawString(moonPhase[ip], 120, 319);
ui.drawBmp("/moon/moonphase_L" + String(icon) + ".bmp", 120 - 30, 318 - 16 - 60);
tft.setTextDatum(BC_DATUM);
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
tft.setTextPadding(0); // Reset padding width to none
tft.drawString(sunStr, 40, 270);
tft.setTextDatum(BR_DATUM);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextPadding(tft.textWidth(" 88:88 "));
String rising = strTime(current->sunrise) + " ";
int dt = rightOffset(rising, ":"); // Draw relative to colon to them aligned
tft.drawString(rising, 40 + dt, 290);
String setting = strTime(current->sunset) + " ";
dt = rightOffset(setting, ":");
tft.drawString(setting, 40 + dt, 305);
tft.setTextDatum(BC_DATUM);
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
tft.drawString(cloudStr, 195, 260);
String cloudCover = "";
cloudCover += current->clouds;
cloudCover += "%";
tft.setTextDatum(BR_DATUM);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextPadding(tft.textWidth(" 100%"));
tft.drawString(cloudCover, 210, 277);
tft.setTextDatum(BC_DATUM);
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
tft.drawString(humidityStr, 195, 300 - 2);
String humidity = "";
humidity += current->humidity;
humidity += "%";
tft.setTextDatum(BR_DATUM);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextPadding(tft.textWidth("100%"));
tft.drawString(humidity, 210, 315);
tft.setTextPadding(0); // Reset padding width to none
}
/***************************************************************************************
** Získejte název souboru ikony z indexového čísla
***************************************************************************************/
const char* getMeteoconIcon(uint16_t id, bool today)
{
if ( today && id/100 == 8 && (current->dt < current->sunrise || current->dt > current->sunset)) id += 1000;
if (id/100 == 2) return "thunderstorm";
if (id/100 == 3) return "drizzle";
if (id/100 == 4) return "unknown";
if (id == 500) return "lightRain";
else if (id == 511) return "sleet";
else if (id/100 == 5) return "rain";
if (id >= 611 && id <= 616) return "sleet";
else if (id/100 == 6) return "snow";
if (id/100 == 7) return "fog";
if (id == 800) return "clear-day";
if (id == 801) return "partly-cloudy-day";
if (id == 802) return "cloudy";
if (id == 803) return "cloudy";
if (id == 804) return "cloudy";
if (id == 1800) return "clear-night";
if (id == 1801) return "partly-cloudy-night";
if (id == 1802) return "cloudy";
if (id == 1803) return "cloudy";
if (id == 1804) return "cloudy";
return "unknown";
}
/***************************************************************************************
** Nakreslete oddělovací čáru části obrazovky
***************************************************************************************/
// if you don't want separators, comment out the tft-line
void drawSeparator(uint16_t y) {
tft.drawFastHLine(10, y, 240 - 2 * 10, 0x4228);
}
/***************************************************************************************
** Určete místo, kde se má čára rozdělit
***************************************************************************************/
// determine the "space" split point in a long string
int splitIndex(String text)
{
uint16_t index = 0;
while ( (text.indexOf(' ', index) >= 0) && ( index <= text.length() / 2 ) ) {
index = text.indexOf(' ', index) + 1;
}
if (index) index--;
return index;
}
/***************************************************************************************
** Odsazení na pravé straně znaku
***************************************************************************************/
// Vypočítá deltu souřadnic od konce textu Řetězec po začátek podřetězce obsaženého v tomto textu
// Lze použít k vertikálnímu zarovnání textu, takže například dvojtečka „:“ v časové hodnotě je vždy
// vykreslení ve stejném bodě na obrazovce bez ohledu na různé proporcionální šířky znaků,
// lze také použít k zarovnání desetinných míst pro úhledné formátování
int rightOffset(String text, String sub)
{
int index = text.indexOf(sub);
return tft.textWidth(text.substring(index));
}
/***************************************************************************************
** Posunutí levé strany k znaku
***************************************************************************************/
// Vypočítá deltu souřadnic od začátku textu String po začátek podřetězce obsaženého v tomto textu
// Lze použít ke svislému zarovnání textu doleva, například dvojtečka „:“ v časové hodnotě je vždy
// vykreslení ve stejném bodě na obrazovce bez ohledu na různé proporcionální šířky znaků,
// lze také použít k zarovnání desetinných míst pro úhledné formátování
int leftOffset(String text, String sub)
{
int index = text.indexOf(sub);
return tft.textWidth(text.substring(0, index));
}
/***************************************************************************************
** Nakreslete kruhový segment
***************************************************************************************/
// Nakreslete segment kruhu, vycentrovaný na x, y s definovaným počátečním_úhlem a podřízeným pod_úhlem
// Úhly jsou definovány ve směru hodinových ručiček s 0 nahoře
// Segment má poloměr r a je vykreslen v definované barvě
// Lze použít pro výsečové grafy atd., V tomto náčrtu se používá pro směr větru
#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 segment start for next segment
x1 = x2;
y1 = y2;
}
}
/***************************************************************************************
** Vytiskněte informace o počasí na sériový monitor
***************************************************************************************/
void printWeather(void)
{
#ifdef SERIAL_MESSAGES
Serial.println("Weather from OpenWeather\n");
Serial.println("############### Current weather ###############\n");
Serial.print("dt (time) : "); Serial.println(strDate(current->dt));
Serial.print("sunrise : "); Serial.println(strDate(current->sunrise));
Serial.print("sunset : "); Serial.println(strDate(current->sunset));
Serial.print("main : "); Serial.println(current->main);
Serial.print("tepl : "); Serial.println(current->temp);
Serial.print("vlhkost : "); Serial.println(current->humidity);
Serial.print("tlak : "); Serial.println(current->pressure);
Serial.print("vítr_speed : "); Serial.println(current->wind_speed);
Serial.print("vítr_deg : "); Serial.println(current->wind_deg);
Serial.print("mraky : "); Serial.println(current->clouds);
Serial.print("id : "); Serial.println(current->id);
Serial.println();
Serial.println("############### Daily weather ###############\n");
Serial.println();
for (int i = 0; i < 5; i++)
{
Serial.print("dt (time) : "); Serial.println(strDate(daily->dt[i]));
Serial.print("id : "); Serial.println(daily->id[i]);
Serial.print("tepl_max : "); Serial.println(daily->temp_max[i]);
Serial.print("tepl_min : "); Serial.println(daily->temp_min[i]);
Serial.println();
}
#endif
}
/***************************************************************************************
** Převést čas Unixu na časový řetězec „místní datum“ „12:34“
***************************************************************************************/
String strTime(time_t unixTime)
{
time_t local_time = TIMEZONE.toLocal(unixTime, &tz1_Code);
String localTime = "";
if (hour(local_time) < 10) localTime += "0";
localTime += hour(local_time);
localTime += ":";
if (minute(local_time) < 10) localTime += "0";
localTime += minute(local_time);
return localTime;
}
/***************************************************************************************
Převést čas Unixu na místní datum + řetězec času „16. října 17:18“, končí novým řádkem
***************************************************************************************/
String strDate(time_t unixTime)
{
time_t local_time = TIMEZONE.toLocal(unixTime, &tz1_Code);
String localDate = "";
localDate += day(local_time);
localDate += ". ";
localDate += monthStr(month(local_time));
localDate += " ";
localDate += dayStr(weekday(local_time));
localDate += "";
return localDate;
}