#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
//#include <GxEPD2_BW.h>
#include <GxEPD2_3C.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
//#include <Adafruit_GFX.h>
#include <Wire.h>
#include <Fonts/FreeMonoBold9pt7b.h>
#define ENABLE_GxEPD2_GFX 0
#define BME280_POWER_PIN 0
// MOSI: GPIO 11, 35, 36, 37 (najczęściej 11 lub 35)
// SCK: GPIO 12, 13, 14, 15, 16, 17, 18, 19, 20, 21
#define EPD_SCK 12
#define EPD_MOSI 11
#define EPD_MISO -1
#define EPD_CS 10
#define EPD_DC 9
#define EPD_RST 46
#define EPD_BUSY 3
// GDEM029C90 128x296, SSD1680 SCK(18), MISO(19), MOSI(23), SS(5)
GxEPD2_3C<GxEPD2_290_C90c, GxEPD2_290_C90c::HEIGHT> display(GxEPD2_290_C90c(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY)); // GDEM029C90 128x296, SSD1680
//GxEPD2_BW<GxEPD2_290, GxEPD2_290::HEIGHT> display(GxEPD2_290(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));
//GxEPD2_3C<GxEPD2_290c, GxEPD2_290c::HEIGHT> display(GxEPD2_290c(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY));
//GxEPD2_3C<GxEPD2_290_Z13c, GxEPD2_290_Z13c::HEIGHT> display(GxEPD2_290_Z13c(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY)); // GDEH029Z13
//GxEPD2_3C<GxEPD2_290_C90c, GxEPD2_290_C90c::HEIGHT> display(GxEPD2_290_C90c(EPD_CS, EPD_DC, EPD_RST, EPD_BUSY)); // GDEM029C90
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* WeatherURL = "https://api.openweathermap.org/data/2.5/weather?lat=45.37&lon=10.19&appid=dbd9ecc273c56776e5aed832c0cf6fc2&lang=pl";
const char* DayForcastURL = "https://api.openweathermap.org/data/2.5/forecast?q=Caino,it&appid=dbd9ecc273c56776e5aed832c0cf6fc2&lang=pl";
//https://api.openweathermap.org/data/2.5/weather?lat=45.37&lon=10.19&appid=dbd9ecc273c56776e5aed832c0cf6fc2
//https://api.openweathermap.org/data/2.5/weather?q=Caino,it&appid=dbd9ecc273c56776e5aed832c0cf6fc2
//https://api.openweathermap.org/data/2.5/weather?lat=45.37&lon=10.19&appid=dbd9ecc273c56776e5aed832c0cf6fc2&lang=pl
//https://api.openweathermap.org/data/2.5/forecast?lat=45.37&lon=10.19&appid=dbd9ecc273c56776e5aed832c0cf6fc2
//https://api.openweathermap.org/data/2.5/forecast?q=Caino,it&appid=dbd9ecc273c56776e5aed832c0cf6fc2&lang=pl
//https://api.openweathermap.org/data/2.5/onecall?lat=45.37&lon=10.19&exclude=minutely&appid=dbd9ecc273c56776e5aed832c0cf6fc2
// kody błędów muszą być liczbami dodatnimi
#define ERROR_WEBSITE 1
#define ERROR_BAD_DATA 2
// Obecnie nie ma 100 różnych typów, ale powinno to objąć wszelkie zmiany w przyszłości
#define NUM_OF_WEATHERS 100
// jeśli true, wyświetl prędkość wiatru w milach na godzinę, w przeciwnym razie w metrach na sekundę
#define MPH false
// obecnie maksimum to 4, większy ekran może pozwolić na więcej
#define NUM_DAYS_FORCAST 4
// Rzeczy o głębokim śnie
// Współczynnik konwersji mikrosekund na sekundy
#define uS_TO_S_FACTOR 1000000
// współczynnik konwersji minut na sekundy
#define min_to_s_FACTOR 60
// Godzina, o której ESP32 przejdzie w tryb uśpienia (w minutach)
#define TIME_TO_SLEEP 20
// Ustawienia stron internetowych i dostępu do Internetu itp.
const char* WebSiteError = "Błąd ze strony internetowej OpenWeather.";
const char* DataError = "Dane ze strony internetowej są nieprawidłowe.";
const char* UnknownError = "Nieznany kod błędu:";
// globalne zmienne temperatury
float main_temp, main_temp_min, main_temp_max, WindSpeed;
int main_pressure, main_humidity, WindAngle;
String WeatherDesc;
uint16_t WeatherID;
// Identyfikatory pogody na następne cztery dni
uint16_t ForecastID[4];
// Identyfikatory pogody na następne 12 godzin
uint16_t ThreeHourlyForcastID[4];
// Czas na kolejne 4 prognozy pogody w ciągu 3 godzin (dwie cyfry godziny)
String ThreeHourlyForcastTime[4];
struct tm * DateTime;
// przechowuj czytelną dla człowieka datę prognozy
char NowTimeStr[80];
bool BME280Found = true;
// struktura do przechowywania opisów pogody i ich identyfikatorów
struct Weather_Struct {
uint16_t ID;
const char* Line1;
const char* Line2;
const unsigned char* Gfx;
};
// Grafika dla obróconych, mniejszych nazw dni tygodnia
// 'PON', 14x31px
const unsigned char MonGfx [] PROGMEM = {
0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xe0, 0x1c, 0xe0, 0x1c, 0xfe, 0x3c, 0xf8, 0xfc,
0xe3, 0xfc, 0xe0, 0x1c, 0xe0, 0x1c, 0xff, 0xfc, 0xfc, 0xfc, 0xf0, 0x3c, 0xe3, 0x1c, 0xef, 0xdc,
0xef, 0xdc, 0xef, 0xdc, 0xe3, 0x1c, 0xf0, 0x3c, 0xff, 0xfc, 0xfb, 0xfc, 0xe1, 0xfc, 0xe4, 0xfc,
0xee, 0xfc, 0xee, 0xfc, 0xe0, 0x1c, 0xe0, 0x1c, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc
};
// 'WTO', 14x31px
const unsigned char TueGfx [] PROGMEM = {
0xff, 0xfc, 0xff, 0xfc, 0xf8, 0x7c, 0xf0, 0x3c, 0xe7, 0x9c, 0xef, 0xdc, 0xef, 0xdc, 0xe7, 0x9c,
0xf0, 0x3c, 0xf8, 0x7c, 0xff, 0xfc, 0xef, 0xfc, 0xef, 0xfc, 0xe0, 0x1c, 0xe0, 0x1c, 0xef, 0xfc,
0xef, 0xfc, 0xef, 0xfc, 0xe7, 0xfc, 0xe0, 0x7c, 0xfc, 0x1c, 0xff, 0x1c, 0xf0, 0x3c, 0xe3, 0xfc,
0xe0, 0xfc, 0xfe, 0x1c, 0xff, 0x1c, 0xe0, 0x1c, 0xe1, 0xfc, 0xff, 0xfc, 0xff, 0xfc
};
// 'ŚRO', 14x31px
const unsigned char WedGfx [] PROGMEM = {
0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xfc, 0xfc, 0xf0, 0x3c, 0xe3, 0x1c, 0xef, 0xdc, 0xef, 0xdc,
0xef, 0xdc, 0xe3, 0x1c, 0xf0, 0x3c, 0xff, 0xfc, 0xff, 0xdc, 0xf1, 0x9c, 0xe0, 0x1c, 0xee, 0x7c,
0xee, 0xfc, 0xe0, 0x1c, 0xe0, 0x1c, 0xff, 0xfc, 0xff, 0xfc, 0xe6, 0x3c, 0xec, 0x1c, 0xac, 0xdc,
0xec, 0xdc, 0xe0, 0xdc, 0xf1, 0x9c, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc
};
// 'CZW', 14x31px
const unsigned char ThuGfx [] PROGMEM = {
0xff, 0xfc, 0xff, 0xfc, 0xe3, 0xfc, 0xe0, 0x7c, 0xfe, 0x1c, 0xff, 0x1c, 0xf0, 0x7c, 0xe3, 0xfc,
0xe0, 0xfc, 0xfe, 0x1c, 0xff, 0x1c, 0xe0, 0x3c, 0xe3, 0xfc, 0xff, 0xfc, 0xe7, 0xdc, 0xe3, 0xdc,
0xe9, 0xdc, 0xec, 0xdc, 0xee, 0x5c, 0xef, 0x1c, 0xef, 0x9c, 0xff, 0xfc, 0xef, 0xdc, 0xef, 0xdc,
0xef, 0xdc, 0xef, 0xdc, 0xf0, 0x1c, 0xf0, 0x3c, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc
};
// 'PIĄ', 14x31px
const unsigned char FriGfx [] PROGMEM = {
0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xf4, 0xff, 0xd4, 0xfe, 0x04, 0xf0, 0x3c,
0xe1, 0x7c, 0xe3, 0x7c, 0xf0, 0x7c, 0xfc, 0x1c, 0xff, 0x9c, 0xff, 0xfc, 0xef, 0xdc, 0xe0, 0x1c,
0xe0, 0x1c, 0xef, 0xdc, 0xff, 0xfc, 0xfb, 0xfc, 0xe1, 0xfc, 0xe4, 0xfc, 0xee, 0xfc, 0xee, 0xfc,
0xe0, 0x1c, 0xe0, 0x1c, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc, 0xff, 0xfc
};
// 'SOB', 14x31px
const unsigned char SatGfx [] PROGMEM = {
0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xf2, 0x3e, 0xe0, 0x1e, 0xed, 0xde, 0xed, 0xde,
0xe0, 0x1e, 0xe0, 0x1e, 0xff, 0xfe, 0xff, 0xfe, 0xf0, 0x3e, 0xf0, 0x3e, 0xef, 0xde, 0xef, 0xde,
0xef, 0xde, 0xe7, 0x9e, 0xf0, 0x3e, 0xf8, 0x7e, 0xff, 0xfe, 0xe6, 0x3e, 0xec, 0x1e, 0xec, 0xde,
0xec, 0xde, 0xe0, 0xde, 0xf1, 0x9e, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe
};
// 'NIE', 14x31px
const unsigned char SunGfx [] PROGMEM = {
0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xef, 0xde, 0xed, 0xde, 0xed, 0xde,
0xed, 0xde, 0xe0, 0x1e, 0xe0, 0x1e, 0xff, 0xfe, 0xff, 0xfe, 0xef, 0xde, 0xe0, 0x1e, 0xe0, 0x1e,
0xef, 0xde, 0xff, 0xfe, 0xff, 0xfe, 0xe0, 0x1e, 0xe0, 0x1e, 0xfe, 0x7e, 0xf8, 0xfe, 0xe3, 0xfe,
0xe0, 0x1e, 0xe0, 0x1e, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe, 0xff, 0xfe
};
// Weather Icons
// 'HeavyRain', 31x31px
const unsigned char HeavyRain [] PROGMEM = {
0x00, 0x3f, 0x00, 0x00, 0x00, 0x7f, 0x80, 0x00, 0x00, 0xff, 0xc0, 0x00, 0x01, 0xff, 0xe0, 0x00,
0x01, 0xff, 0xf0, 0x00, 0x01, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xc0, 0x0f, 0xff, 0xff, 0xe0,
0x3f, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, 0xfe,
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfc, 0x00, 0x00, 0x00, 0x00,
0x18, 0x06, 0x31, 0x98, 0x38, 0xce, 0x73, 0xb8, 0x31, 0xcc, 0x63, 0x30, 0x71, 0x8c, 0xe3, 0x30,
0x63, 0x80, 0xcc, 0x00, 0x07, 0x00, 0x1c, 0xc0, 0x0e, 0x30, 0xd9, 0xc0, 0x1c, 0x71, 0xd9, 0x80,
0x18, 0x61, 0x81, 0x80, 0x00, 0x61, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00
};
// 'Rain', 31x31px
const unsigned char Rain [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x7f, 0x80, 0x00, 0x00, 0xff, 0xc0, 0x00,
0x01, 0xff, 0xe0, 0x00, 0x01, 0xff, 0xf0, 0x00, 0x01, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xc0,
0x0f, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xfc,
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfc,
0x00, 0x00, 0x00, 0x00, 0x18, 0x06, 0x00, 0x60, 0x38, 0x0e, 0x00, 0xe0, 0x30, 0x0c, 0x00, 0xc0,
0x70, 0x0c, 0x18, 0xc0, 0x60, 0xc0, 0x38, 0x00, 0x01, 0xc0, 0x30, 0x00, 0x01, 0x80, 0x30, 0x00,
0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// 'LightRain', 31x31px
const unsigned char LightRain [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x7b, 0x80, 0x00, 0x00, 0xd5, 0xc0, 0x00,
0x01, 0xee, 0xe0, 0x00, 0x01, 0x55, 0x70, 0x00, 0x01, 0xbb, 0xbf, 0x80, 0x03, 0x55, 0x5d, 0xc0,
0x0e, 0xee, 0xee, 0xe0, 0x3d, 0x55, 0x55, 0x60, 0x7b, 0xbb, 0xbb, 0xa0, 0x55, 0x55, 0x55, 0x7c,
0xee, 0xee, 0xee, 0xee, 0xd5, 0x55, 0x55, 0x54, 0xbb, 0xbb, 0xbb, 0xba, 0xd5, 0x55, 0x55, 0x54,
0xee, 0xee, 0xee, 0xee, 0xd5, 0x55, 0x55, 0x56, 0xfb, 0xbb, 0xbb, 0xbe, 0x7f, 0xff, 0xff, 0xfc,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x30, 0x60, 0x00,
0x00, 0x70, 0x60, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// 'Cloudy', 31x31px
const unsigned char Cloudy [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x7f, 0x80, 0x00, 0x00, 0xff, 0xc0, 0x00,
0x01, 0xff, 0xe0, 0x00, 0x01, 0xff, 0xf0, 0x00, 0x01, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xc0,
0x0f, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xfc,
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe,
0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0xfe, 0x7f, 0xff, 0xff, 0xfc,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// 'LightCloud', 31x31px
const unsigned char LightCloud [] PROGMEM = {
0x00, 0x3f, 0x00, 0x00, 0x00, 0x61, 0x80, 0x00, 0x00, 0xc0, 0xc0, 0x00, 0x01, 0x80, 0x60, 0x00,
0x01, 0x00, 0x20, 0x00, 0x01, 0x00, 0x3f, 0x80, 0x03, 0x00, 0x10, 0x80, 0x0e, 0x00, 0x00, 0xc0,
0x38, 0x3f, 0x00, 0x40, 0x60, 0x61, 0x80, 0x40, 0x40, 0x40, 0xc0, 0x7c, 0xc0, 0xc0, 0x40, 0x06,
0x80, 0x80, 0x7e, 0x02, 0x81, 0x80, 0x02, 0x02, 0x87, 0x00, 0x02, 0x02, 0x8c, 0x00, 0x03, 0x06,
0xd8, 0x00, 0x01, 0xe4, 0x70, 0x00, 0x00, 0x2c, 0x30, 0x00, 0x00, 0x38, 0x10, 0x00, 0x00, 0x20,
0x10, 0x00, 0x00, 0x60, 0x18, 0x00, 0x00, 0xc0, 0x0f, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// 'MostlyCloudy', 31x31px
const unsigned char MostlyCloudy [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00,
0x00, 0xff, 0x80, 0x00, 0x01, 0xff, 0x80, 0x00, 0x01, 0xff, 0xfc, 0x00, 0x03, 0xff, 0xfc, 0x00,
0x0f, 0xff, 0xfc, 0x00, 0x1f, 0xff, 0x5e, 0x00, 0x3f, 0xfe, 0xaf, 0xc0, 0x3f, 0xfd, 0x57, 0xc0,
0x3f, 0xfe, 0xab, 0xe0, 0x3f, 0xfd, 0x57, 0x70, 0x3f, 0xfa, 0xaa, 0xb0, 0x3f, 0xf5, 0x55, 0x50,
0x1f, 0xaa, 0xaa, 0xbc, 0x01, 0xd5, 0x55, 0x56, 0x01, 0xaa, 0xaa, 0xaa, 0x01, 0x55, 0x55, 0x54,
0x01, 0xaa, 0xaa, 0xaa, 0x01, 0x55, 0x55, 0x56, 0x01, 0xea, 0xaa, 0xae, 0x00, 0x7f, 0xff, 0xfc,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// 'ScatteredCloud', 31x31px
const unsigned char ScatteredCloud [] PROGMEM = {
0x00, 0x3f, 0x00, 0x00, 0x00, 0x6b, 0x80, 0x00, 0x00, 0xd5, 0xc0, 0x00, 0x01, 0xaa, 0xe0, 0x00,
0x01, 0x55, 0x70, 0x00, 0x01, 0xaa, 0xbf, 0x80, 0x03, 0x55, 0x5d, 0xc0, 0x0e, 0xaa, 0xaa, 0xe0,
0x3d, 0x55, 0x55, 0x60, 0x6a, 0xaa, 0xaa, 0xa0, 0x55, 0x57, 0xf5, 0x7c, 0xea, 0xae, 0xba, 0xae,
0xd5, 0x5d, 0x5d, 0x54, 0xaa, 0xaa, 0xae, 0xaa, 0xd5, 0x5d, 0x57, 0xd4, 0xaa, 0xba, 0xae, 0xea,
0xd5, 0x75, 0x55, 0x76, 0xeb, 0xea, 0xaa, 0xae, 0x7f, 0x55, 0x55, 0x7c, 0x03, 0xaa, 0xaa, 0xae,
0x03, 0x55, 0x55, 0x56, 0x02, 0xaa, 0xaa, 0xaa, 0x03, 0x55, 0x55, 0x56, 0x02, 0xaa, 0xaa, 0xae,
0x03, 0xd5, 0x55, 0x5c, 0x00, 0xff, 0xff, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// 'ThunderStorm', 31x31px
const unsigned char ThunderStorm [] PROGMEM = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x7f, 0x80, 0x00, 0x00, 0xff, 0xc0, 0x00,
0x01, 0xff, 0xe0, 0x00, 0x01, 0xff, 0xf0, 0x00, 0x01, 0xff, 0xff, 0x80, 0x03, 0xff, 0xff, 0xc0,
0x0f, 0xff, 0xff, 0xe0, 0x3f, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xf8, 0x7c,
0xff, 0xff, 0xf0, 0xfe, 0xff, 0xff, 0xf0, 0xfe, 0xff, 0x0f, 0xe1, 0xfe, 0xfe, 0x1f, 0xe0, 0x7e,
0xfe, 0x1f, 0xc0, 0xfe, 0xfc, 0x3f, 0xf9, 0xfe, 0xfc, 0x0f, 0xf3, 0xfe, 0x78, 0x1f, 0xf3, 0xfc,
0x0f, 0x30, 0x26, 0x00, 0x02, 0x60, 0x6c, 0x00, 0x06, 0x40, 0x48, 0x00, 0x04, 0xc0, 0xd8, 0x00,
0x0d, 0x80, 0xb0, 0x00, 0x09, 0x00, 0xe0, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// 'Clear', 31x31px
const unsigned char Clear [] PROGMEM = {
0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x08, 0x20, 0x01, 0x00, 0x10, 0x10, 0x01, 0x00, 0x20,
0x08, 0x01, 0x00, 0x40, 0x04, 0x00, 0x00, 0x80, 0x02, 0x07, 0xe1, 0x00, 0x01, 0x3c, 0x3c, 0x00,
0x00, 0x60, 0x06, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x80, 0x01, 0x00, 0x01, 0x80, 0x01, 0x80,
0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x7d, 0x00, 0x00, 0xbe,
0x01, 0x00, 0x00, 0x80, 0x01, 0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x00, 0x80, 0x01, 0x00,
0x00, 0xc0, 0x03, 0x00, 0x00, 0x60, 0x06, 0x00, 0x00, 0x3c, 0x3c, 0x00, 0x01, 0x07, 0xe1, 0x00,
0x02, 0x00, 0x00, 0x80, 0x04, 0x01, 0x00, 0x40, 0x08, 0x01, 0x00, 0x20, 0x10, 0x01, 0x00, 0x10,
0x20, 0x01, 0x00, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
// 'Fog', 31x31px
const unsigned char Fog [] PROGMEM = {
0x00, 0x00, 0x08, 0x00, 0x20, 0x06, 0x08, 0x00, 0x24, 0x00, 0x00, 0x48, 0x02, 0x00, 0x00, 0x50,
0x00, 0x11, 0x8c, 0x04, 0x00, 0x11, 0x80, 0x04, 0x30, 0x00, 0x00, 0x00, 0x30, 0x60, 0x01, 0x80,
0x00, 0x60, 0xc0, 0x00, 0x00, 0x02, 0xc0, 0x20, 0x18, 0x02, 0x0c, 0x20, 0x03, 0x00, 0x0c, 0x00,
0x03, 0x18, 0x02, 0x00, 0x00, 0x18, 0x80, 0x00, 0x00, 0x00, 0x04, 0x64, 0x30, 0x00, 0x08, 0x04,
0x10, 0x40, 0x80, 0x00, 0x02, 0x41, 0x0c, 0x00, 0x04, 0x00, 0x04, 0x06, 0x20, 0x01, 0x00, 0x80,
0x20, 0x02, 0x00, 0x80, 0x16, 0x00, 0x10, 0x20, 0x00, 0x00, 0x90, 0x20, 0x20, 0x21, 0x00, 0x00,
0x24, 0x20, 0x00, 0x80, 0x08, 0x00, 0x08, 0x80, 0x00, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x10,
0x30, 0x01, 0x00, 0x48, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x00
};
// 'Snow', 31x31px
const unsigned char Snow [] PROGMEM = {
0x00, 0x33, 0x98, 0x00, 0x02, 0x1b, 0xb0, 0x00, 0x07, 0x0f, 0xe0, 0xc0, 0x07, 0x07, 0xc0, 0x80,
0x07, 0x03, 0xc0, 0x80, 0x47, 0x83, 0x80, 0x84, 0x77, 0x81, 0x01, 0xcc, 0x3f, 0x81, 0x01, 0xf8,
0x1f, 0x81, 0x01, 0xf0, 0x0f, 0x81, 0x03, 0xe0, 0x1f, 0xc3, 0x87, 0xf0, 0x3f, 0xef, 0xef, 0xf8,
0xf0, 0x7f, 0xff, 0x9e, 0x40, 0x3e, 0x78, 0x06, 0x00, 0x1c, 0x38, 0x00, 0x00, 0x18, 0x18, 0x00,
0x00, 0x18, 0x18, 0x00, 0xf0, 0x1c, 0x38, 0x1e, 0xf0, 0x3e, 0x7c, 0x1e, 0x7e, 0xef, 0xee, 0xfc,
0x7f, 0xcf, 0xc7, 0xf0, 0x07, 0x83, 0x83, 0xe0, 0x0f, 0x83, 0x01, 0xf8, 0x3f, 0x83, 0x01, 0xf8,
0xf7, 0x83, 0x01, 0xdc, 0xf7, 0x83, 0x81, 0xc4, 0x07, 0x03, 0x81, 0xc0, 0x07, 0x07, 0xc1, 0xc0,
0x07, 0x0f, 0xe1, 0xc0, 0x07, 0x1d, 0xb0, 0x00, 0x00, 0x39, 0x98, 0x00
};
// Vertical time GFx
// '00', 14x31px
const unsigned char Time0 [] PROGMEM = {
0xff, 0xff, 0xff, 0xfe, 0xe7, 0x9f, 0xf3, 0xce, 0xc7, 0x0f, 0xe1, 0x86, 0x9b, 0x6f, 0xcd, 0xb6,
0xbb, 0x66, 0xcd, 0xb2, 0xbb, 0x66, 0xdd, 0xb2, 0xbb, 0x67, 0xdd, 0xba, 0xbb, 0x67, 0xdd, 0xba,
0xbb, 0x67, 0xdd, 0xb2, 0xbb, 0x67, 0xcd, 0xb2, 0x93, 0x6f, 0xcd, 0xb6, 0xc7, 0x0e, 0xe1, 0x86,
0xe7, 0x9e, 0xf3, 0xce, 0xff, 0xff, 0xff, 0xfe
};
// '03', 14x31px
const unsigned char Time3 [] PROGMEM = {
0x07, 0xe0, 0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x07, 0xe0,
0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10,
0x02, 0x10, 0x00, 0x00, 0x06, 0xe0, 0x0f, 0xf0, 0x09, 0x10, 0x09, 0x10, 0x0c, 0x30, 0x04, 0x20,
0x00, 0x00, 0x07, 0xe0, 0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0
};
// '06', 14x31px
const unsigned char Time6 [] PROGMEM = {
0x07, 0xe0, 0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x07, 0xe0,
0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10,
0x02, 0x10, 0x00, 0x00, 0x04, 0xe0, 0x0d, 0xf0, 0x09, 0x10, 0x09, 0x10, 0x0f, 0xf0, 0x07, 0xe0,
0x00, 0x00, 0x07, 0xe0, 0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0
};
// '09', 14x31px
const unsigned char Time9 [] PROGMEM = {
0x07, 0xe0, 0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x07, 0xe0,
0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10,
0x02, 0x10, 0x00, 0x00, 0x07, 0xe0, 0x0f, 0xf0, 0x08, 0x90, 0x08, 0x90, 0x0f, 0xb0, 0x07, 0x20,
0x00, 0x00, 0x07, 0xe0, 0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0
};
// '12', 14x31px
const unsigned char Time12 [] PROGMEM = {
0x07, 0xe0, 0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x07, 0xe0,
0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10,
0x02, 0x10, 0x00, 0x00, 0x07, 0x10, 0x0f, 0x90, 0x08, 0xd0, 0x08, 0x70, 0x0c, 0x30, 0x04, 0x10,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x0f, 0xf0, 0x06, 0x00, 0x02, 0x00
};
// '15', 14x31px
const unsigned char Time15 [] PROGMEM = {
0x07, 0xe0, 0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x07, 0xe0,
0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10,
0x02, 0x10, 0x00, 0x00, 0x08, 0xe0, 0x09, 0xf0, 0x09, 0x10, 0x0d, 0x10, 0x0f, 0x30, 0x03, 0x20,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x0f, 0xf0, 0x06, 0x00, 0x02, 0x00
};
// '18', 14x31px
const unsigned char Time18 [] PROGMEM = {
0x07, 0xe0, 0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x07, 0xe0,
0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10,
0x02, 0x10, 0x00, 0x00, 0x06, 0xe0, 0x0f, 0xf0, 0x09, 0x10, 0x09, 0x10, 0x0f, 0xf0, 0x06, 0xe0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x0f, 0xf0, 0x06, 0x00, 0x02, 0x00
};
// '21', 14x31px
const unsigned char Time21 [] PROGMEM = {
0x07, 0xe0, 0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x07, 0xe0,
0x0f, 0xf0, 0x08, 0x10, 0x08, 0x10, 0x0f, 0xf0, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10,
0x02, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xf0, 0x0f, 0xf0, 0x06, 0x00, 0x02, 0x00,
0x00, 0x00, 0x07, 0x10, 0x0f, 0x90, 0x08, 0xd0, 0x08, 0x70, 0x0c, 0x30, 0x04, 0x10
};
// 'BadTime', 14x31px
const unsigned char BadTime [] PROGMEM = {
0x06, 0x00, 0x0f, 0x00, 0x09, 0xd0, 0x08, 0xd0, 0x0c, 0x00, 0x04, 0x00, 0x00, 0x00, 0x06, 0x00,
0x0f, 0x00, 0x09, 0xd0, 0x08, 0xd0, 0x0c, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x10,
0x02, 0x10, 0x00, 0x00, 0x06, 0x00, 0x0f, 0x00, 0x09, 0xd0, 0x08, 0xd0, 0x0c, 0x00, 0x04, 0x00,
0x00, 0x00, 0x06, 0x00, 0x0f, 0x00, 0x09, 0xd0, 0x08, 0xd0, 0x0c, 0x00, 0x04, 0x00
};
// tablica typów pogody, wypełniana w konfiguracji (za pomocą funkcji) b zmienia się,
// jeśli używasz innej witryny pogodowej lub jeśli dodają do liczby opisów
Weather_Struct Weathers[NUM_OF_WEATHERS];
// liczba różnych warunków pogodowych, sumuje się w miarę dodawania kolejnych w funkcji PopulateWeathers
uint8_t WeatherCount = 0;
// 1K jest wystarczające (zwroty wynoszą około 0,5K)
StaticJsonDocument<1024> Doc;
Adafruit_BME280 bme;
void setup() {
Serial.begin(115200);
delay(500);
// kod konfiguracyjny, aby uruchomić go raz:
unsigned status;
Wire.begin();/*
pinMode(BME280_POWER_PIN, OUTPUT);
// włącz BME280, gdy przejdziemy w stan głębokiego snu, zostanie wyłączony
digitalWrite(BME280_POWER_PIN, true);
status = bme.begin(0x76);
if (!status)
BME280Found = false;*/
//Serial.begin(115200);
WiFi.begin(ssid, password);
Serial.print("Łączę z WiFI");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.print("\nPołączono z siecią Wi-Fi, IP adr: ");
Serial.println(WiFi.localIP());
SPI.begin(EPD_SCK, EPD_MISO, EPD_MOSI, EPD_CS);
//display.init(115200);
display.init(115200, true, 1000, false);
// 0 wyłącza diagnostykę szeregową biblioteki, co czasem przyspiesza start
//display.init(0);
PopulateWeathers();
// zrób raport
WeatherReport();
display.display(); // To wysyła obraz
Serial.println(display.epd2.panel);
display.hibernate(); // To wyłącza zasilanie panelu e-papieru
Serial.println("Zasypiam na 10 minut...");
Serial.flush();
// 600 sekund * 1 000 000 (mikrosekundy)
esp_sleep_enable_timer_wakeup(600ULL * 1000000ULL);
esp_deep_sleep_start();
Serial.println("--- DEBUG ---");
Serial.println(DayForcastURL);
Serial.println("-------------");
}
void WeatherReport() {
uint8_t ErrorCode;
if ((WiFi.status() == WL_CONNECTED)) {
ErrorCode = GetWeather();
if (ErrorCode == 0)
DisplayWeather();
else
DisplayWeatherSiteError(ErrorCode);
}
}
void loop() {
/*
tutaj nie ma nic do zobaczenia, w trybie głębokiego uśpienia (w tym przypadku ustawienia hibernacji)
pętla główna nigdy nie jest wywoływana, ponieważ procesor w efekcie restartuje się po wybudzeniu
i przechodzi ponownie w tryb hibernacji po zakończeniu procedury konfiguracyjnej
*/
}
int16_t IndexOfWeatherID(uint16_t ID) {
uint8_t Idx = 0;
while (Idx < WeatherCount) {
if (Weathers[Idx].ID == ID)
return Idx;
else
Idx++;
}
// dotarłem tak daleko, a potem nie znalazłem
return -1;
}
void PopulateWeathers() {
// Ciągi pogodowe, zaprojektowane tak, aby zajmowały dwie linijki wyświetlacza, pierwsza to pierwsza linijka, druga to druga linijka. Maksymalna liczba znaków dla wyświetlacza o rozdzielczości 200 pikseli to 18 znaków na linijkę.
// Użycie dostarczonych ciągów ze strony internetowej może czasami powodować rozbicie tekstu na dwie linijki, a opisy mogą być…
// czasami „dziwne”. Zastąp poniższy tekst dowolnym innym, nie zmieniając pierwszego parametru – kodu ID, który jest unikalny dla aplikacji pogodowej.
// Identyfikatory dotyczą OpenWeathermap.org.
AddWeather(200, "Burza", "z lekkim deszczem", ThunderStorm);
AddWeather(201, "Burza", "z deszczem", ThunderStorm);
AddWeather(202, "Burza", "z ulewą", ThunderStorm);
AddWeather(210, "Lekka burza", "", ThunderStorm);
AddWeather(211, "Burza", "", ThunderStorm);
AddWeather(212, "Silna", "Burza", ThunderStorm);
AddWeather(221, "Gwałtowna burza", "duże zachmurzenie", ThunderStorm);
AddWeather(230, "Burza", "z lekką mżawką", ThunderStorm);
AddWeather(231, "Burza", "z mżawką", ThunderStorm);
AddWeather(232, "Burza", "z silną mżawką", ThunderStorm);
AddWeather(300, "Lekka mżawka", "", LightRain);
AddWeather(301, "Mżawka", "", LightRain);
AddWeather(302, "Silna mżawka", "", Rain);
AddWeather(310, "Lekka mżawka i", "przemieszny deszcz", Rain);
AddWeather(311, "Mżawka i", "trochę deszczu", LightRain);
AddWeather(312, "Silna mżawka i", "przemieszny deszcz", Rain);
AddWeather(313, "Przelotne opady", "deszczu i mżawki", LightRain);
AddWeather(314, "Silne opady", "deszczu i mżawki", Rain);
AddWeather(321, "Przelotna mżawka", "", LightRain);
AddWeather(500, "Lekki deszcz", "", LightRain);
AddWeather(501, "Umiarkowany deszcz", "", LightRain);
AddWeather(502, "Ulewny deszcz", "", HeavyRain);
AddWeather(503, "Bardzo silna ulewa", "", HeavyRain);
AddWeather(504, "Ekstremalna ulewa", "", HeavyRain);
AddWeather(511, "Marznący deszcz", "", HeavyRain);
AddWeather(520, "Lekkie opady", "deszczu", LightRain);
AddWeather(521, "Przelotny deszcz", "", Rain);
AddWeather(522, "Silne opady", "deszczu", HeavyRain);
AddWeather(531, "Zmienne opady", "duże zachmurzenie", Rain);
AddWeather(600, "Lekki śnieg", "", Snow);
AddWeather(601, "Śnieg", "", Snow);
AddWeather(602, "Silny śnieg", "", Snow);
AddWeather(611, "Śnieg z deszczem", "", Snow);
AddWeather(612, "Lekki śnieg", "z deszczem", Rain);
AddWeather(613, "Śnieżyca", "z deszczem", Rain);
AddWeather(615, "Lekki deszcz", "i śnieg", Snow);
AddWeather(616, "Deszcz ze śniegiem", "", Snow);
AddWeather(620, "Lekkie opady", "śniegu", Snow);
AddWeather(621, "Przelotny śnieg", "", Snow);
AddWeather(622, "Silne opady", "śniegu", Snow);
AddWeather(701, "Zamglenie", "", Fog);
AddWeather(711, "Zadymienie", "", Fog);
AddWeather(721, "Zmętnienie", "", Fog);
AddWeather(731, "Pył / Piasek", "wirujący", Fog);
AddWeather(741, "Mgła", "", Fog);
AddWeather(751, "Piasek", "", Fog);
AddWeather(761, "Pył", "", Fog);
AddWeather(762, "Pył wulkaniczny", "", Fog);
AddWeather(771, "Szkwały", "", Fog);
AddWeather(781, "Tornado", "", Fog);
AddWeather(800, "Bezchmurnie", "", Clear);
AddWeather(801, "Lekkie", "zachmurzenie", LightCloud);
AddWeather(802, "Rozproszone", "chmury", ScatteredCloud);
AddWeather(803, "Przewaga chmur", "pochmurno", MostlyCloudy);
AddWeather(804, "Całkowite", "zachmurzenie", Cloudy);
}
void AddWeather(uint16_t ID, const char* FirstLine, const char* SecondLine, const unsigned char* Gfx) {
Weathers[WeatherCount].ID = ID;
Weathers[WeatherCount].Line1 = FirstLine;
Weathers[WeatherCount].Line2 = SecondLine;
Weathers[WeatherCount].Gfx = Gfx;
WeatherCount++;
}
void DisplayWeatherSiteError(uint8_t ErrorCode) {
const char* ErrorStr;
bool ShowErrorCode = false;
switch (ErrorCode) {
case ERROR_WEBSITE: ErrorStr = WebSiteError; break;
case ERROR_BAD_DATA: ErrorStr = DataError; break;
default: {
ErrorStr = UnknownError;
ShowErrorCode = true;
}
}
// wydrukuj błąd
Serial.println(ErrorStr);
// Nie powinno się to zdarzyć, chyba że programista zapomniał wpisać kod błędu!
if (ShowErrorCode)
Serial.println(ErrorCode);
}
void DisplayWeather() {
// bufor dla ciągów float
static char outstr[20];
static char convstr[8];
int16_t WeatherIdx;
// Wydrukuj wartości
WeatherIdx = IndexOfWeatherID(WeatherID);
display.setRotation(1);
display.setFont(&FreeMonoBold9pt7b);
display.setTextColor(GxEPD_BLACK);
display.setFullWindow();
display.firstPage();
do {
/*
display.fillScreen(GxEPD_WHITE); // 128x296
display.drawRect(0, 0, 296, 128, GxEPD_BLACK);
display.drawFastHLine(0, 18, 296, GxEPD_BLACK);
display.setCursor(120, 15);
CentreText(NowTimeStr);
display.setCursor(20, 32); // 20, 32
if (WeatherIdx != -1) {
CentreString(Weathers[WeatherIdx].Line1);
display.setCursor(2, 48);
CentreString(Weathers[WeatherIdx].Line2);
} else {
CentreString("Nieznana pogoda");
display.setCursor(2, 48);
display.print(" Kod: ");
display.print(WeatherID);
//display.print("ŻÓŁW");
}
display.setCursor(6, 64);
outstr[0] = 0;
strcat(outstr, " T:");
dtostrf(main_temp, 1, 0, convstr);
strcat(outstr, convstr);
strcat(outstr, " "); // symbol zastępczy dla części Deg C
strcat(outstr, " Lo:");
dtostrf(main_temp_min, 1, 0, convstr);
strcat(outstr, convstr);
strcat(outstr, " Hi:");
dtostrf(main_temp_max, 1, 0, convstr);
strcat(outstr, convstr);
uint16_t XPos = CentreString(outstr);
// Teraz oblicz, gdzie powinien znajdować się DEg C i nanieś go na wykres
display.setCursor(XPos + 33, 64);
DegC();
display.setCursor(6, display.getCursorY() + 16);
// prędkość wiatru
outstr[0] = 0;
strcat(outstr, "Wiatr:");
dtostrf(WindSpeed, 1, 0, convstr);
strcat(outstr, convstr);
if (MPH)
strcat(outstr, "mph,");
else
strcat(outstr, "m/s,");
GetDirectionFromBearing(WindAngle, outstr);
CentreString(outstr);
display.setCursor(6, display.getCursorY() + 16);
outstr[0] = 0;
strcat(outstr, "Wilg:");
dtostrf(main_humidity, 1, 0, convstr);
strcat(outstr, convstr);
strcat(outstr, "% Cisn:");
dtostrf(main_pressure, 1, 0, convstr);
strcat(outstr, convstr);
CentreString(outstr);
// wyświetlanie bezpośrednich wartości otoczenia
display.drawFastHLine(0, 296 - 34, 296, GxEPD_BLACK);
display.drawFastHLine(0, 296 - 33, 296, GxEPD_BLACK);
if (BME280Found) {
outstr[0] = 0;
// display.drawRect(296-34,0,296,296,GxEPD_BLACK);
display.setCursor(6, 296 - 20);
strcat(outstr, "Ten pokój T:");
dtostrf(bme.readTemperature(), 1, 0, convstr);
strcat(outstr, convstr);
display.print(outstr);
DegC();
outstr[0] = 0;
strcat(outstr, "Wilg:");
dtostrf(bme.readHumidity(), 1, 0, convstr);
strcat(outstr, convstr);
strcat(outstr, "%");
strcat(outstr, " Cisn:");
dtostrf(bme.readPressure() / 100.0F, 1, 0, convstr);
strcat(outstr, convstr);
display.setCursor(0, 296 - 4);
display.print(outstr);
display.drawFastHLine(0, 296 - 17, 114, GxEPD_BLACK);
display.drawFastVLine(114, 296 - 33, 16, GxEPD_BLACK);
} else {
display.setCursor(0, 120 - 20);
display.println("Czujnik BME280 nie znaleziony!");
}*/
// prognoza narysuj bloki
// --- LEWA STRONA: Prognoza 3-godzinna (4 bloki jeden pod drugim) --- 128x296
for (uint8_t i = 0; i < NUM_DAYS_FORCAST; i++) {
// Obliczamy Y tak, aby rozłożyć 4 ikony na wysokości
uint16_t y_offset = i * 32;
// Czas (X = 1)
display.drawRect(2, y_offset, 14, 31, GxEPD_BLACK);
display.drawBitmap(1, y_offset, ReturnTimeGfx(ThreeHourlyForcastTime[i]), 14, 31, GxEPD_BLACK);
// Ikona (X = 16, żeby nie nachodziła na czas)
display.drawBitmap(16, y_offset, Weathers[IndexOfWeatherID(ThreeHourlyForcastID[i])].Gfx, 31, 31, GxEPD_BLACK);
}
// --- PRAWA STRONA: Prognoza na dni (4 bloki jeden pod drugim) ---
for (uint8_t i = 0; i < NUM_DAYS_FORCAST; i++) {
uint16_t y_offset = i * 32;
// X przesunięty do prawej krawędzi (128 - szerokość_ikony - tekst)
// 128 - 31 (ikona) - 14 (tekst) - margines = ok. 80
uint16_t x_start_right = 246;
// Nazwa dnia
display.drawRect(x_start_right, y_offset, 14, 31, GxEPD_BLACK);
display.drawBitmap(x_start_right, y_offset, ReturnDayGfx(i + (DateTime->tm_wday + 1)), 14, 31, GxEPD_BLACK);
// Ikona pogody obok nazwy dnia
display.drawBitmap(x_start_right + 15, y_offset, Weathers[IndexOfWeatherID(ForecastID[i])].Gfx, 31, 31, GxEPD_BLACK);
}
// Linie oddzielające (opcjonalnie, dla estetyki)
display.drawFastVLine(63, 35, 180, GxEPD_BLACK); // Pionowa linia na środku
/*for (uint16_t i = 0; i < 2; i++)
display.drawFastHLine(0, 0 + (i * 33), 200, GxEPD_BLACK); // 0, 0 + (i * 33), 200
for (uint16_t i = 0; i < 4; i++)
display.drawFastVLine(i * 50, 100, 66, GxEPD_BLACK); // i * 50, 100, 66
// wyświetl prognozę na kilka następnych godzin (interwały 3-godzinne)
// NUM_DAYS_FORCAST ma taką samą wartość dla liczby prognoz na ten dzień
for (uint8_t i = 0; i < NUM_DAYS_FORCAST; i++) {
// mieścić w porze dnia
display.drawBitmap(1 + (i * 50), 101, ReturnTimeGfx(ThreeHourlyForcastTime[i]), 14, 31, GxEPD_BLACK);
// Prognoza gfx
display.drawBitmap(15 + (i * 50), 100, Weathers[IndexOfWeatherID(ThreeHourlyForcastID[i])].Gfx, 31, 31, GxEPD_BLACK);
}
// wyświetl prognozę na następne kilka dni
for (uint8_t i = 0; i < NUM_DAYS_FORCAST; i++) {
// wpisz nazwy dni tygodnia
display.drawBitmap(1 + (i * 50), 135, ReturnDayGfx(i + (DateTime->tm_wday + 1)), 14, 31, GxEPD_BLACK);
// Prognoza gfx
display.drawBitmap(15 + (i * 50), 135, Weathers[IndexOfWeatherID(ForecastID[i])].Gfx, 31, 31, GxEPD_BLACK);
}*/
}
while (display.nextPage());
}
const unsigned char* ReturnTimeGfx(String Time) {
// Czas będzie godziną w formacie 24-godzinnym, tj. 00, 01, 02 itd. do 23
if (Time == "00") return Time0;
if (Time == "03") return Time3;
if (Time == "06") return Time6;
if (Time == "09") return Time9;
if (Time == "12") return Time12;
if (Time == "15") return Time15;
if (Time == "18") return Time18;
if (Time == "21") return Time21;
return BadTime;
}
const unsigned char* ReturnDayGfx(uint8_t DayOfWeek) {
// gdzie 0 oznacza niedzielę, zawija liczbę, tak że np. 7 zwróci niedzielę
// Wykonuje zawijanie, pobierając resztę z dzielenia
DayOfWeek = DayOfWeek % 7;
// który będzie numerem dnia
switch (DayOfWeek) {
case (0): return SunGfx; break;
case (1): return MonGfx; break;
case (2): return TueGfx; break;
case (3): return WedGfx; break;
case (4): return ThuGfx; break;
case (5): return FriGfx; break;
case (6): return SatGfx; break;
}
}
uint16_t CentreText(char *TheString) {
// działa tylko z używaną czcionką
uint16_t X;
X = 100 - float((strlen(TheString) * 11) / 2);
display.setCursor(X, display.getCursorY());
display.print(TheString);
return X;
}
uint16_t CentreString(String TheString) {
// działa tylko z używaną czcionką
uint16_t X;
X = 100 - float((TheString.length() * 11) / 2);
display.setCursor(X, display.getCursorY());
display.print(TheString);
return X;
}
void DegC() {
// wyświetla oC w bieżącej lokalizacji
display.setCursor(display.getCursorX(), display.getCursorY() - 4);
display.print("o");
display.setCursor(display.getCursorX(), display.getCursorY() + 4);
display.print("C");
}
uint8_t GetWeather() {
// zwraca 0, jeśli wszystko poszło dobrze, w przeciwnym razie kod błędu
HTTPClient http;
time_t UnixTime;
JsonObject Forcast, WeatherDoc;
// wystarczająco dużo, aby poradzić sobie z codziennymi zadaniami i prognozami
const size_t capacity = 30000;
http.begin(WeatherURL);
int httpCode = http.GET();
if (httpCode > 0) {
String payload = http.getString();
// utwórz obiekt JSON przekazując wymaganą pamięć
DynamicJsonDocument Doc(capacity);
// Wygeneruj dane z serializowanych danych tekstowych zwróconych ze strony internetowej
DeserializationError error = deserializeJson(Doc, payload);
if (error) {
// odkomentuj problemy z debugowaniem danych
// Serial.println(error.c_str());
return ERROR_BAD_DATA;
}
WeatherDoc = Doc["weather"][0];
// WeatherDesc=WeatherDoc["opis"];
// Te wiersze rozwiązują problem z ciągami znaków i biblioteką JSON
WeatherDesc = (const char*)WeatherDoc["description"];
WeatherDesc = WeatherDoc["description"].as<const char*>();
WeatherDesc = WeatherDoc["description"].as<String>();
WeatherID = WeatherDoc["id"];
WindSpeed = Doc["wind"]["speed"];
UnixTime = Doc["dt"];
DateTime = gmtime(&UnixTime);
// Czas w formacie brytyjskim: dzień miesiąca, potem miesiąc, a potem rok, zakomentuj i usuń komentarz z wersji amerykańskiej, aby uzyskać inny styl
strftime (NowTimeStr, 80, "%d/%m/%Y %R", DateTime);
// US version
//strftime (NowTimeStr,80,"%m/%d/%Y %R",DateTime);
if (MPH)
// przekonwertować na mile na godzinę wymagane
WindSpeed *= 2.237;
WindSpeed = round(WindSpeed);
WindAngle = Doc["wind"]["deg"];
JsonObject main = Doc["main"];
main_temp = main["temp"];
main_pressure = main["pressure"];
main_humidity = main["humidity"];
main_temp_min = main["temp_min"];
main_temp_max = main["temp_max"];
// przelicz z Kelvinów na stopnie C
main_temp -= 273.15;
main_temp = round(main_temp);
main_temp_min -= 273.15;
main_temp_min = round(main_temp_min);
main_temp_max -= 273.15;
main_temp_max = round(main_temp_max);
} else
return ERROR_WEBSITE;
// Uwolnij zasoby
http.end();
// Uzyskaj prognozę pogody na kolejne cztery dni i kilka godzin
http.begin(DayForcastURL);
httpCode = http.GET();
if (httpCode > 0) {
String payload = http.getString();
// Poniższy wiersz został wygenerowany przez narzędzie online do obliczania rozmiaru bufora dla zwróconych danych.
// Ponieważ jednak nie kontrolujemy tych danych zwrotnych i mogą one wzrosnąć w przyszłości, dodałem 1000 na końcu wiersza zamiast sugerowanych 410.
// To na wszelki wypadek, nie chcemy żadnych przekroczeń bufora.
// utwórz obiekt JSON przekazując wymaganą pamięć
DynamicJsonDocument Doc(capacity);
// Wygeneruj dane z serializowanych danych tekstowych zwróconych ze strony internetowej
DeserializationError error = deserializeJson(Doc, payload);
if (error) {
Serial.print(F("deserializeJson() failed: "));
// debug
Serial.println(error.c_str());
return ERROR_BAD_DATA;
}
// Zbierz wszystkie różne prognozy pogody w jednym miejscu
JsonArray List = Doc["list"];
JsonObject Data;
int16_t WeatherIdx;
char TimeBuffer[80];
String ForcastTime;
// uzyskaj prognozy na kolejne 4 trzy godziny, są one ułożone w kolejności na liście zwrotnej, więc to takie proste
for (uint8_t Idx = 0; Idx < NUM_DAYS_FORCAST; Idx++) {
Data = List[Idx];
WeatherDoc = Data["weather"][0];
ThreeHourlyForcastID[Idx] = WeatherDoc["id"];
// Te wiersze rozwiązują problem z ciągami znaków i biblioteką JSON
ForcastTime = (const char*)Data["dt_txt"];
ForcastTime = Data["dt_txt"].as<const char*>();
ForcastTime = Data["dt_txt"].as<String>();
ThreeHourlyForcastTime[Idx] = ForcastTime.substring(11, 13);
}
// Następnie otrzymaj prognozę na kolejne cztery dni
for (uint8_t Idx = 0; Idx < NUM_DAYS_FORCAST; Idx++) {
ForecastDate(DateTime, 1);
strftime (TimeBuffer, 80, "%F 12:00:00", DateTime);
WeatherIdx = GetWeatherData(List, TimeBuffer);
if (WeatherIdx >= 0) {
Data = List[WeatherIdx];
WeatherDoc = Data["weather"][0];
ForecastID[Idx] = WeatherDoc["id"];
const char* TheDate = Data["dt_txt"];
} else
return ERROR_WEBSITE;
}
// powrót do oryginału
DateTime = gmtime(&UnixTime);
} else
return ERROR_WEBSITE;
// Uwolnij zasoby
http.end();
// wszystko dobrze
return 0;
}
void ForecastDate(tm *TheDate, uint8_t DaysInFuture) {
const time_t ONE_DAY = 24 * 60 * 60 ;
// Sekundy od początku epoki
time_t date_seconds = mktime( TheDate ) + (DaysInFuture * ONE_DAY) ;
// Zaktualizuj datę dzwoniącego
// Użyj czasu lokalnego, ponieważ mktime konwertuje się na UTC, więc data może ulec zmianie
*TheDate = *localtime( &date_seconds ) ; ;
}
int16_t GetWeatherData(JsonArray List, const char* DateTime) {
// biorąc pod uwagę główną tablicę danych elementów pogodowych, zwraca indeks obiektu danych dla podanej daty i godziny
// zwraca -1, jeśli nie znaleziono
JsonObject ThisEntry;
uint16_t ListSize, ListIdx;
bool Found = false;
ThisEntry = List[0];
const char* TheDate = ThisEntry["dt_txt"];
ListIdx = 0;
ListSize = List.size();
while ((ListIdx < ListSize) & (Found == false)) {
ThisEntry = List[ListIdx];
if (strcmp(ThisEntry["dt_txt"], DateTime) == 0)
Found = true;
else
ListIdx++;
}
if (Found)
return ListIdx;
else
return -1;
}
void GetDirectionFromBearing(uint16_t Bearing, char* Direction) {
// Zwraca tekstową wersję azymutu w stopniach. Należy pamiętać, że zwraca tylko 8 podstawowych kierunków,
// bardziej szczegółowe informacje mogą być mylące dla przeciętnego użytkownika.
// i tak naprawdę nie wymaga aż takiego poziomu szczegółowości, można go rozszerzyć o więcej kierunków
// podziel stopnie przez 45.
static char convstr[8];
uint8_t SimpleDirection = round(Bearing / 45);
switch (SimpleDirection) {
case 0: strcat(Direction, "Północ"); break; // North
case 1: strcat(Direction, "Płn.-Wsch."); break; // North East
case 2: strcat(Direction, "Wschód"); break; // East
case 3: strcat(Direction, "Płd.-Wsch."); break; // South East
case 4: strcat(Direction, "Południe"); break; // South
case 5: strcat(Direction, "Płd.-Zach."); break; // South West
case 6: strcat(Direction, "Zachód"); break; // West
case 7: strcat(Direction, "Płn.-Zach."); break; // North West
case 8: strcat(Direction, "Północ"); break; // North
default: {
//nigdy nie powinno tu dotrzeć!
strcat(Direction, "???");
dtostrf(Bearing, 1, 0, convstr);
strcat(Direction, convstr);
}
}
}Loading
esp32-s3-devkitc-1
esp32-s3-devkitc-1