#include <SD.h>
#define SERIAL_MESSAGES // Dla raportów pogodowych na wyjściu szeregowym
//#define SCREEN_SERVER // Do zrzucania (dumpowania) zrzutów ekranu z TFT
//#define RANDOM_LOCATION // Tylko do testów, wybiera losową lokalizację pogodową przy każdym odświeżeniu
//#define FORMAT_LittleFS // Czyści LittleFS i wszystkie pliki!
const char* PROGRAM_VERSION = "ESP32 CYD OpenWeatherMap LittleFS V02";
#include <FS.h>
#include <LittleFS.h>
#define AA_FONT_SMALL "fonts/NSBold15" // 15 point Noto sans serif bold
#define AA_FONT_LARGE "fonts/NSBold36" // 36 point Noto sans serif bold
// Ładowanie bibliotek i ustawień
#include <Arduino.h>
#include <SPI.h>
#include <TFT_eSPI.h> // https://github.com/Bodmer/TFT_eSPI
// Dodatkowe funkcje
#include "GfxUi.h" // Dołączone do tego szkicu
// Wybierz bibliotekę do załadowania
#include <WiFi.h>
// sprawdź All_Settings.h w celu dostosowania do Twoich potrzeb
#include "All_Settings.h"
#include <JSON_Decoder.h> // https://github.com/Bodmer/JSON_Decoder
#include <OpenWeather.h> // Najnowsze tutaj: https://github.com/Bodmer/OpenWeather
#include "NTP_Time.h" // Dołączone do tego szkicu, zobacz tę kartę dla potrzeb bibliotek
// Biblioteka korekcji strefy czasowej: https://github.com/JChristensen/Timezone
// Definiowanie zmiennych globalnych i instancji klas
// Wywołanie niestandardowej biblioteki
TFT_eSPI tft = TFT_eSPI();
#include "esp_partition.h"
void dumpLittlefsPartitions() {
auto it = esp_partition_find(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_LITTLEFS, NULL);
if (!it) {
Serial.println("Brak partycji LITTLEFS w tabeli!");
return;
}
while (it) {
const esp_partition_t* p = esp_partition_get(it);
Serial.printf("LFS part: label=%s, addr=0x%06x, size=%u\n", p->label, p->address, p->size);
it = esp_partition_next(it);
}
esp_partition_iterator_release(it);
}
// Instancja biblioteki prognozy pogody
OW_Weather ow;
OW_forecast* forecast;
boolean booted = true;
// Funkcje Jpeg i bmpDraw
GfxUi ui = GfxUi(&tft);
long lastDownloadUpdate = millis();
// Deklaracja prototypów
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);
int getNextDayIndex(void);
bool tft_output(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t* bitmap) {
// Zatrzymanie dalszego dekodowania, ponieważ obraz wykracza poza dół ekranu
if (y >= tft.height())
return 0;
// Ta funkcja automatycznie przytnie renderowanie bloku obrazu na granicach wyświetlacza TFT
tft.pushImage(x, y, w, h, bitmap);
// Zwróć 1, aby dekodować następny blok
return 1;
}
// Konfiguracja (Setup)
void setup() {
Serial.begin(250000);
SD.begin(5);
delay(500);
Serial.println(PROGRAM_VERSION);
tft.begin();
// Dla ekranu 320x480
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
dumpLittlefsPartitions();
if (!LittleFS.begin()) {
Serial.println("Inicjalizacja systemu plików Flash nie powiodła się!");
while (1) yield();
}
Serial.println("\nSystem plików Flash dostępny!");
// Włącz, jeśli chcesz skasować LittleFS, to zajmuje trochę czasu!
// następnie wyłącz i wgraj szkic ponownie, aby uniknąć ponownego formatowania przy każdym uruchomieniu!
#ifdef FORMAT_LittleFS
// Punkt odniesienia: Środek Dół
tft.setTextDatum(BC_DATUM);
tft.drawString("Formatowanie LittleFS, czekaj!", 120, 195);
LittleFS.format();
#endif
TJpgDec.setJpgScale(1);
TJpgDec.setCallback(tft_output);
// Może być konieczna zamiana bajtów kolorów jpg (kolejność bajtów)
TJpgDec.setSwapBytes(true);
// Rysowanie ekranu powitalnego
if (LittleFS.exists("/splash/OpenWeather.jpg") == true) {
TJpgDec.drawFsJpg(0, 40, "/splash/OpenWeather.jpg", LittleFS);
}
delay(2000);
// Czyszczenie dolnej sekcji ekranu
tft.fillRect(0, 206, 240, 320 - 206, TFT_BLACK);
tft.loadFont(AA_FONT_SMALL, LittleFS);
// Punkt odniesienia: Środek Dół
tft.setTextDatum(BC_DATUM);
tft.setTextColor(TFT_LIGHTGREY, TFT_BLACK);
tft.drawString("Oryginał: blog.squix.org", 120, 260);
tft.drawString("Adaptacja: Bodmer", 120, 280);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
delay(2000);
tft.fillRect(0, 206, 240, 320 - 206, TFT_BLACK);
tft.drawString("Łączenie z WiFi", 120, 240);
// Wypełnienie następnego tekstu drawString() do pełnej szerokości, aby nadpisać stary tekst
tft.setTextPadding(240);
// Wywołaj raz dla ESP32
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
uint32_t t0 = millis();
while (WiFi.status() != WL_CONNECTED && (millis() - t0) < 15000) {
Serial.print(".");
delay(250);
}
Serial.println(WiFi.status() == WL_CONNECTED ? "OK" : "UPŁYNĄŁ CZAS");
Serial.println();
tft.setTextDatum(BC_DATUM);
// Wypełnienie następnego tekstu drawString() do pełnej szerokości, aby nadpisać stary tekst
tft.setTextPadding(240);
// Czyszczenie linii powyżej za pomocą ustawionej szerokości wypełnienia
tft.drawString(" ", 120, 220);
tft.drawString("Pobieranie danych pogodowych...", 120, 240);
// Pobranie czasu
udp.begin(localPort);
syncTime();
tft.unloadFont();
}
// Pętla (Loop)
void loop() {
// Sprawdzenie, czy powinniśmy zaktualizować informacje o pogodzie
if (booted || (millis() - lastDownloadUpdate > 1000UL * UPDATE_INTERVAL_SECS)) {
updateData();
lastDownloadUpdate = millis();
}
// Jeśli zmieniła się minuta, zażądaj nowego czasu z serwera NTP
if (booted || minute() != lastMinute) {
// Najpierw zaktualizuj wyświetlany czas, ponieważ być może będziemy musieli czekać na odpowiedź
drawTime();
lastMinute = minute();
// Żądanie i synchronizacja lokalnego zegara
syncTime();
#ifdef SCREEN_SERVER
screenServer();
#endif
}
booted = false;
}
// Pobranie danych pogodowych i aktualizacja ekranu
// Aktualizacja informacji z Internetu i ekranu
void updateData() {
// booted = true; // Tylko do testów
// booted = false; // Tylko do testów
if (booted)
drawProgress(20, "Aktualizowanie czasu...");
else
fillSegment(22, 22, 0, (int)(20 * 3.6), 16, TFT_NAVY);
if (booted)
drawProgress(50, "Aktualizowanie warunków...");
else
fillSegment(22, 22, 0, (int)(50 * 3.6), 16, TFT_NAVY);
// Utworzenie struktury przechowującej pobraną pogodę
forecast = new OW_forecast;
// Losowy wybór miejsca na Ziemi do testowania ikon itp.
#ifdef RANDOM_LOCATION
String latitude = "";
latitude = (random(180) - 90);
String longitude = "";
longitude = (random(360) - 180);
Serial.print("Szer. geogr. = ");
Serial.print(latitude);
Serial.print(", Dł. geogr. = ");
Serial.println(longitude);
#endif
bool parsed = ow.getForecast(forecast, api_key, latitude, longitude, units, language);
if (parsed)
Serial.println("Otrzymano punkty danych");
else Serial.println("Nie udało się pobrać punktów danych");
//Serial.print("Wolna sterta = "); Serial.println(ESP.getFreeHeap(), DEC);
// Do debugowania, włącz wyjście za pomocą #define SERIAL_MESSAGES
printWeather();
if (booted) {
drawProgress(100, "Gotowe...");
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) {
tft.loadFont(AA_FONT_SMALL, LittleFS);
drawCurrentWeather();
drawForecast();
drawAstronomy();
tft.unloadFont();
// Aktualizacja temperatury tutaj, abyśmy nie musieli ciągle
// ładować i rozładowywać czcionki, co zajmuje czas
tft.loadFont(AA_FONT_LARGE, LittleFS);
tft.setTextDatum(TR_DATUM);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
// Kod ASCII czcionki 0xB0 to symbol stopnia, ale w małej czcionce używane jest 'o'
// Maksymalna szerokość wartości
tft.setTextPadding(tft.textWidth(" -88"));
String weatherText = "";
// Zrób z tego temperaturę całkowitą
weatherText = String(forecast->temp[0], 0);
// + "°" symbol jest duży... użyj o w małej czcionce
tft.drawString(weatherText, 215, 95);
tft.unloadFont();
} else {
Serial.println("Nie udało się pobrać pogody");
}
// Usuń, aby zwolnić miejsce
delete forecast;
}
// Aktualizacja paska postępu
void drawProgress(uint8_t percentage, String text) {
tft.loadFont(AA_FONT_SMALL, LittleFS);
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);
tft.unloadFont();
}
// Rysowanie cyfr zegara
void drawTime() {
tft.loadFont(AA_FONT_LARGE, LittleFS);
// Konwersja UTC na czas lokalny, zwraca kod strefy w tz1_Code, np. "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);
tft.setTextDatum(BC_DATUM);
tft.setTextColor(TFT_YELLOW, TFT_BLACK);
// Szerokość ciągu znaków + margines
tft.setTextPadding(tft.textWidth(" 44:44 "));
tft.drawString(timeNow, 120, 53);
drawSeparator(51);
tft.setTextPadding(0);
tft.unloadFont();
}
// Rysowanie aktualnej pogody
void drawCurrentWeather() {
time_t local_time = TIMEZONE.toLocal(now(), &tz1_Code);
// String date = "Updated: " + strDate(local_time);
// zobacz problem https://github.com/Bodmer/OpenWeather/issues/26
String date = "Aktualizacja: " + strDate(now());
String weatherText = "Brak";
tft.setTextDatum(BC_DATUM);
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
tft.setTextPadding(tft.textWidth(" Aktualizacja: Mmm 44 44:44 ")); // Szerokość ciągu znaków + margines
tft.drawString(date, 120, 16);
String weatherIcon = "";
String currentSummary = forecast->main[0];
currentSummary.toLowerCase();
weatherIcon = getMeteoconIcon(forecast->id[0], true);
ui.drawBmp("/icon/" + weatherIcon + ".bmp", 0, 53);
// Tekst Pogody
if (language == "pl")
weatherText = forecast->main[0];
else
weatherText = forecast->description[0];
tft.setTextDatum(BR_DATUM);
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
int splitPoint = 0;
int xpos = 235;
splitPoint = splitIndex(weatherText);
// xpos - szerokość ikony
tft.setTextPadding(xpos - 100);
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("oC", 237, 95);
else
tft.drawString("oF", 237, 95);
// Duże cyfry temperatury dodane w updateData() do zaoszczędzenia na zamianie czcionki tutaj
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
weatherText = String(forecast->wind_speed[0], 0);
if (units == "metric")
weatherText += " m/s";
else
weatherText += " mph";
tft.setTextDatum(TC_DATUM);
// Maksymalna długość ciągu znaków?
tft.setTextPadding(tft.textWidth("888 m/s"));
tft.drawString(weatherText, 124, 136);
if (units == "imperial") {
weatherText = forecast->pressure[0];
weatherText += " in";
} else {
weatherText = String(forecast->pressure[0], 0);
weatherText += " hPa";
}
tft.setTextDatum(TR_DATUM);
// Maksymalna długość ciągu znaków?
tft.setTextPadding(tft.textWidth(" 8888hPa"));
tft.drawString(weatherText, 230, 136);
int windAngle = (forecast->wind_deg[0] + 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);
// Reset punktu odniesienia do normalnego
tft.setTextDatum(TL_DATUM);
// Reset szerokości wypełnienia do braku
tft.setTextPadding(0);
}
// Rysowanie 4 kolumn prognozy
// rysuje kolumny prognozy
void drawForecast() {
int8_t dayIndex = getNextDayIndex();
drawForecastDetail(8, 171, dayIndex);
dayIndex += 8;
drawForecastDetail(66, 171, dayIndex); // było 95
dayIndex += 8;
drawForecastDetail(124, 171, dayIndex); // było 180
dayIndex += 8;
drawForecastDetail(182, 171, dayIndex); // było 180
drawSeparator(171 + 69);
}
// Rysowanie 1 kolumny prognozy na x, y
// pomocnik dla kolumn prognozy
void drawForecastDetail(uint16_t x, uint16_t y, uint8_t dayIndex) {
if (dayIndex >= MAX_DAYS * 8)
return;
String day = shortDOW[weekday(TIMEZONE.toLocal(forecast->dt[dayIndex + 4], &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"));
// Znalezienie minimalnej i maksymalnej temperatury w ciągu dnia
float tmax = -9999;
float tmin = 9999;
for (int i = 0; i < 8; i++)
if (forecast->temp_max[dayIndex + i] > tmax)
tmax = forecast->temp_max[dayIndex + i];
for (int i = 0; i < 8; i++)
if (forecast->temp_min[dayIndex + i] < tmin)
tmin = forecast->temp_min[dayIndex + i];
String highTemp = String(tmax, 0);
String lowTemp = String(tmin, 0);
tft.drawString(highTemp + " " + lowTemp, x + 25, y + 17);
String weatherIcon = getMeteoconIcon(forecast->id[dayIndex + 4], false);
ui.drawBmp("/icon50/" + weatherIcon + ".bmp", x, y + 18);
// Reset szerokości wypełnienia do braku
tft.setTextPadding(0);
}
// Rysowanie wschodu/zachodu słońca, Księżyca, zachmurzenia i wilgotności
void drawAstronomy() {
tft.setTextDatum(BC_DATUM);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextPadding(tft.textWidth(" Last qtr "));
time_t local_time = TIMEZONE.toLocal(forecast->dt[0], &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);
// Reset szerokości wypełnienia do braku
tft.setTextPadding(0);
tft.drawString("Słońce", 40, 270);
tft.setTextDatum(BR_DATUM);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextPadding(tft.textWidth(" 88:88 "));
String rising = strTime(forecast->sunrise) + " ";
// Rysowanie względem dwukropka, aby były wyrównane
int dt = rightOffset(rising, ":");
tft.drawString(rising, 40 + dt, 290);
String setting = strTime(forecast->sunset) + " ";
dt = rightOffset(setting, ":");
tft.drawString(setting, 40 + dt, 305);
tft.setTextDatum(BC_DATUM);
tft.setTextColor(TFT_ORANGE, TFT_BLACK);
tft.drawString("Chmury", 195, 260); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ?
String cloudCover = "";
cloudCover += forecast->clouds_all[0];
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("Wilgotność", 195, 300 - 2); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ?
String humidity = "";
humidity += forecast->humidity[0];
humidity += "%";
tft.setTextDatum(BR_DATUM);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextPadding(tft.textWidth("100%"));
tft.drawString(humidity, 210, 315);
// Reset szerokości wypełnienia do braku
tft.setTextPadding(0);
}
// Pobranie nazwy pliku ikony z numeru indeksu
const char* getMeteoconIcon(uint16_t id, bool today) {
// if ( today && id/100 == 8 && (forecast->dt[0] < forecast->sunrise || forecast->dt[0] > forecast->sunset)) id += 1000;
if (today && id / 100 == 8 && (now() < forecast->sunrise || now() > forecast->sunset))
id += 1000;
// zobacz problem https://github.com/Bodmer/OpenWeather/issues/26
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";
}
// Rysowanie linii separatora sekcji ekranu
// jeśli nie chcesz separatorów, skomentuj linię tft
void drawSeparator(uint16_t y) {
tft.drawFastHLine(10, y, 240 - 2 * 10, 0x4228);
}
// Określenie miejsca podziału linii
// określenie punktu podziału "spacją" w długim ciągu znaków
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;
}
// Przesunięcie prawej strony do znaku
// Obliczenie różnicy współrzędnych od końca ciągu znaków tekstu do początku podciągu zawartego w tym tekście
// Może być użyte do pionowego wyrównania tekstu do prawej, tak aby na przykład dwukropek ":" w wartości czasu był zawsze
// wykreślany w tym samym punkcie na ekranie, niezależnie od różnej szerokości znaków,
// mogłoby być również użyte do wyrównania przecinków dziesiętnych dla schludnego formatowania
int rightOffset(String text, String sub) {
int index = text.indexOf(sub);
return tft.textWidth(text.substring(index));
}
// Przesunięcie lewej strony do znaku
// Obliczenie różnicy współrzędnych od początku ciągu znaków tekstu do początku podciągu zawartego w tym tekście
// Może być użyte do pionowego wyrównania tekstu do lewej, tak aby na przykład dwukropek ":" w wartości czasu był zawsze
// wykreślany w tym samym punkcie na ekranie, niezależnie od różnej szerokości znaków,
// mogłoby być również użyte do wyrównania przecinków dziesiętnych dla schludnego formatowania
int leftOffset(String text, String sub) {
int index = text.indexOf(sub);
return tft.textWidth(text.substring(0, index));
}
// Rysowanie segmentu koła
// Rysowanie segmentu koła, wyśrodkowanego na x,y z zdefiniowanym kątem początkowym i rozwartym kątem podrzędnym
// Kąty są zdefiniowane w kierunku zgodnym z ruchem wskazówek zegara, a 0 na górze
// Segment ma promień r i jest kreślony w zdefiniowanym kolorze
// Może być użyty do wykresów kołowych itp., w tym szkicu jest użyty do kierunku wiatru
// Współczynnik konwersji stopni na radiany
#define DEG2RAD 0.0174532925
// Minimalny kąt rozwarty segmentu i przyrost kąta kreślenia (w stopniach)
#define INC 2
void fillSegment(int x, int y, int start_angle, int sub_angle, int r, unsigned int colour) {
// Obliczenie pierwszej pary współrzędnych dla początku segmentu
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;
// Rysowanie bloków koloru co INC stopni
for (int i = start_angle; i < start_angle + sub_angle; i += INC) {
// Obliczenie pary współrzędnych dla końca segmentu
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);
// Kopiowanie końca segmentu do początku segmentu dla następnego segmentu
x1 = x2;
y1 = y2;
}
}
// Pobranie 3-godzinnego indeksu na początku następnego dnia
int getNextDayIndex(void) {
int index = 0;
String today = forecast->dt_txt[0].substring(8, 10);
for (index = 0; index < 9; index++) {
if (forecast->dt_txt[index].substring(8, 10) != today)
break;
}
return index;
}
// Wypisanie informacji o pogodzie do Monitora Szeregowego
void printWeather(void) {
#ifdef SERIAL_MESSAGES
Serial.println("Pogoda z OpenWeather\n");
Serial.print("nazwa_miasta : ");
Serial.println(forecast->city_name);
Serial.print("wschód_słońca : ");
Serial.println(strTime(forecast->sunrise));
Serial.print("zachód_słońca : ");
Serial.println(strTime(forecast->sunset));
Serial.print("Szerokość geograficzna : ");
Serial.println(ow.lat);
Serial.print("Długość geograficzna : ");
Serial.println(ow.lon);
// Możemy użyć strefy czasowej do ostatecznego ustawienia przesunięcia...
Serial.print("Strefa czasowa : ");
Serial.println(forecast->timezone);
Serial.println();
if (forecast) {
Serial.println("############### Prognoza pogody ###############\n");
for (int i = 0; i < (MAX_DAYS * 8); i++) {
Serial.print("3-godzinna prognoza ");
if (i < 10) Serial.print(" ");
Serial.print(i);
Serial.println();
Serial.print("dt (czas) : ");
Serial.println(strTime(forecast->dt[i]));
Serial.print("temp : ");
Serial.println(forecast->temp[i]);
Serial.print("temp.min : ");
Serial.println(forecast->temp_min[i]);
Serial.print("temp.max : ");
Serial.println(forecast->temp_max[i]);
Serial.print("ciśnienie : ");
Serial.println(forecast->pressure[i]);
Serial.print("poziom_morza : ");
Serial.println(forecast->sea_level[i]);
Serial.print("poziom_gruntu : ");
Serial.println(forecast->grnd_level[i]);
Serial.print("wilgotność : ");
Serial.println(forecast->humidity[i]);
Serial.print("zachmurzenie : ");
Serial.println(forecast->clouds_all[i]);
Serial.print("prędkość_wiatru : ");
Serial.println(forecast->wind_speed[i]);
Serial.print("kierunek_wiatru : ");
Serial.println(forecast->wind_deg[i]);
Serial.print("poryw_wiatru : ");
Serial.println(forecast->wind_gust[i]);
Serial.print("widoczność : ");
Serial.println(forecast->visibility[i]);
Serial.print("pop : ");
Serial.println(forecast->pop[i]);
Serial.println();
Serial.print("dt_txt : ");
Serial.println(forecast->dt_txt[i]);
Serial.print("id : ");
Serial.println(forecast->id[i]);
Serial.print("główne : ");
Serial.println(forecast->main[i]);
Serial.print("opis : ");
Serial.println(forecast->description[i]);
Serial.print("ikona : ");
Serial.println(forecast->icon[i]);
Serial.println();
}
}
#endif
}
// Konwersja czasu Unix na ciąg znaków czasu "czas lokalny" "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;
}
// Konwersja czasu Unix na ciąg znaków daty + czasu lokalnego "Paź 16 17:18", kończy się nową linią
String strDate(time_t unixTime) {
time_t local_time = TIMEZONE.toLocal(unixTime, &tz1_Code);
String localDate = "";
localDate += monthShortStr(month(local_time));
localDate += " ";
localDate += day(local_time);
localDate += " " + strTime(unixTime);
return localDate;
}