#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 <U8g2_for_Adafruit_GFX.h>
#include <Wire.h>
#include <weathericons.h>
#include "Climacons30x30.h"
#include "f.h"
#include "weathericons_regular_webfont10pt7b.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
U8G2_FOR_ADAFRUIT_GFX u8g2;
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:";
const char* daysOfWeek[] = { "NIE", "PON", "WTO", "ŚRO", "CZW", "PIĄ", "SOB" };
//const char* daysOfWeek[] = { "NIEDZIELA", "PONIEDZIAŁEK, "WTOREK", "ŚRODA", "CZWARTEK", "PIĄ"TEK, "SOBOTA" };
// 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];
// godzina + noc/dzień
uint8_t currentHour = DateTime->tm_hour;
bool currentIsNight = (currentHour < 6 || currentHour >= 20);
// struktura pogody
struct Weather_Struct {
uint16_t ID;
const char* Line1;
const char* Line2;
unsigned char IconD;
unsigned char IconN;
unsigned char IconChar(bool isNight) const {
return isNight ? IconN : IconD;
}
};
// 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);
display.setRotation(1);
u8g2.begin(display);
// 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.
// Grupa 2xx: Burza
AddWeather(200, "Burza z lekkim deszczem", "", 0x44, 0x20);
AddWeather(201, "Burza z deszczem", "", 0x45, 0x20);
AddWeather(202, "Burza z ulewą", "", 0x46, 0x20);
AddWeather(210, "Lekka burza", "", 0x47, 0x20);
AddWeather(211, "Burza", "", 0x48, 0x20);
AddWeather(212, "Silna burza", "", 0x49, 0x20);
AddWeather(221, "Gwałtowna burza", "duże zachmurzenie", 0x4A, 0x20);
AddWeather(230, "Burza z lekką mżawką", "", 0x4B, 0x20);
AddWeather(231, "Burza z mżawką", "", 0x4C, 0x20);
AddWeather(232, "Burza z silną mżawką", "", 0x4D, 0x20);
// Grupa 3xx: Mżawka
AddWeather(300, "Lekka mżawka", "", 0x26, 0x20);
AddWeather(301, "Mżawka", "", 0x27, 0x20); // drizzle, pioggerella
AddWeather(302, "Silna mżawka", "", 0x28, 0x20);
AddWeather(310, "Lekki deszcz", "z mżawką", 0x29, 0x20);
AddWeather(311, "Deszcz z mżawką", "", 0x2A, 0x20);
AddWeather(312, "Silny deszcz", "z mżawką", 0x2B, 0x20);
AddWeather(313, "Ulewa z mżawką", "", 0x2C, 0x20);
AddWeather(314, "Silna ulewa", "z mżawką", 0x2D, 0x20);
AddWeather(321, "Mżawka przelotna", "", 0x2E, 0x20);
// Grupa 5xx: Deszcz
AddWeather(500, "Lekki deszcz", "", 0x2F, 0x20);
AddWeather(501, "Umiarkowany deszcz", "", 0x30, 0x20);
AddWeather(502, "Silny deszcz", "", 0x31, 0x20);
AddWeather(503, "Bardzo silny deszcz", "", 0x32, 0x20);
AddWeather(504, "Ekstremalna ulewa", "", 0x33, 0x20);
AddWeather(511, "Marznący deszcz", "", 0x34, 0x20);
AddWeather(520, "Lekki deszcz przelotny", "", 0x35, 0x20);
AddWeather(521, "Deszcz przelotny", "", 0x36, 0x20);
AddWeather(522, "Silny deszcz przelotny", "", 0x37, 0x20);
AddWeather(531, "Gwałtowny deszcz", "przelotny", 0x38, 0x20);
// Grupa 6xx: Śnieg
AddWeather(600, "Lekki śnieg", "", 0x39, 0x20);
AddWeather(601, "Śnieg", "", 0x3A, 0x20);
AddWeather(602, "Silny śnieg", "", 0x3B, 0x20);
AddWeather(611, "Deszcz ze śniegiem", "", 0x3C, 0x20);
AddWeather(612, "Lekki deszcz ze śniegiem", "", 0x3D, 0x20);
AddWeather(613, "Deszcz ze śniegiem", "przelotny", 0x3E, 0x20);
AddWeather(615, "Lekki deszcz ze śniegiem", "", 0x3F, 0x20);
AddWeather(616, "Śnieg z deszczem", "", 0x40, 0x20);
AddWeather(620, "Lekki śnieg przelotny", "", 0x41, 0x20);
AddWeather(621, "Śnieg przelotny", "", 0x42, 0x20);
AddWeather(622, "Silny śnieg przelotny", "", 0x43, 0x20);
// Grupa 7xx: Atmosfera
AddWeather(701, "Zamglenie", "", 0x4E, 0x20);
AddWeather(711, "Dym", "", 0x4F, 0x20); // smoke
AddWeather(721, "Mgła / Zamglenie", "", 0x50, 0x20); // fog
AddWeather(731, "Wiry pyłowe", "lub piaskowe", 0x51, 0x20); // dust or sand whirls
AddWeather(741, "Gęsta mgła", "", 0x52, 0x20);
AddWeather(751, "Piasek", "", 0x53, 0x20); // sand
AddWeather(761, "Pył", "", 0x54, 0x20); // dust
AddWeather(762, "Pył wulkaniczny", "", 0x55, 0x20); // volcanic ash
AddWeather(771, "Szkwał", "", 0x56, 0x20);
AddWeather(781, "Tornado", "", 0x57, 0x20);
// Grupa 800: Czyste niebo
AddWeather(800, "Bezchmurnie", "", 0x21, 0x48);
// Grupa 80x: Chmury
AddWeather(801, "Małe zachmurzenie", "11-25%", 0x22, 0x48);
AddWeather(802, "Średnie chmury", "25-50%", 0x23, 0x48);
AddWeather(803, "Duże zachmurzenie", "51-84%", 0x24, 0x48);
AddWeather(804, "Całkowite zachmurzenie", "85-100%", 0x25, 0x48);
}
void AddWeather(uint16_t ID, const char* fLine, const char* sLine, unsigned char IconCharD, unsigned char IconCharN) {
Weathers[WeatherCount].ID = ID;
Weathers[WeatherCount].Line1 = fLine;
Weathers[WeatherCount].Line2 = sLine;
Weathers[WeatherCount].IconD = IconCharD;
Weathers[WeatherCount].IconN = IconCharN;
WeatherCount++;
}
unsigned char GetWeatherIcon(uint16_t id, bool isNight) {
int idx = IndexOfWeatherID(id);
if (idx < 0) return '?'; // fallback
return isNight ? Weathers[idx].IconN : Weathers[idx].IconD;
}
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;////WeatherID = 200;
// Wydrukuj wartości
WeatherIdx = IndexOfWeatherID(WeatherID);
//u8g2.setRotation(1);
//display.setFont(&FreeMonoBold9pt7b);
//display.setTextColor(GxEPD_BLACK);
display.setFullWindow();
display.firstPage();
//u8g2.setForegroundColor(GxEPD_WHITE);czarny tekst
//u8g2.setFont(u8g2_font_t0_22_me);
do {
display.fillScreen(GxEPD_WHITE); // 128x296
//u8g2.setFont(u8g2_font_helvR10_te);
//u8g2.setFont(u8g2_font_12x6LED);
u8g2.setFontMode(0);
u8g2.setFontDirection(0);
u8g2.setForegroundColor(GxEPD_BLACK);
u8g2.setBackgroundColor(GxEPD_WHITE);
display.drawFastHLine(45, 0, 206, GxEPD_BLACK);
display.drawFastHLine(45, 17, 206, GxEPD_BLACK);
//display.drawFastVLine(148, 15, 50, GxEPD_BLACK);
u8g2.setFont(u8g2_font_helvB12_te); // ok cyfry
u8g2.setCursor(148, 15);
CentreText(NowTimeStr);
u8g2.setFont(u8g2_font_unifont_t_polish); // 8x16 Podstawowy, wszystkie znaki
//
u8g2.setCursor(148, 30); // 20, 32
if (WeatherIdx != -1) {
CentreString(Weathers[WeatherIdx].Line1);
u8g2.setCursor(148, 45);
CentreString(Weathers[WeatherIdx].Line2);
} else {
CentreString("Nieznana pogoda");
u8g2.setCursor(148, 50);
CentreString("Kod: ");
u8g2.print(WeatherID);
}/*
display.setCursor(50, 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
u8g2.setFont(u8g2_font_t0_11_te);
u8g2.setFontDirection(3);
// LEWA STRONA: Prognoza 3-godzinna (4 bloki jeden pod drugim) 128x296
display.drawRect(0, 0, 45, 128, GxEPD_BLACK);
for (uint8_t i = 0; i < NUM_DAYS_FORCAST; i++) {
uint16_t y_offset = i * 32;
// CZAS
uint8_t hour = ThreeHourlyForcastTime[i].toInt(); // tylko raz
bool isNight = (hour < 6 || hour >= 20);
// przygotowanie tekstu dla wyświetlenia
String timeText = String(hour) + ":00";
u8g2.drawStr(10, y_offset + 30, timeText.c_str());
// IKONA
int idx = IndexOfWeatherID(ThreeHourlyForcastID[i]);
display.setFont(&weathericons_regular_webfont10pt7b);
display.setTextColor(GxEPD_BLACK);
display.setCursor(13, y_offset + 32);
display.write(Weathers[idx].IconChar(isNight));
// LINIA POD BLOKIEM
display.drawFastHLine(0, y_offset, 45, GxEPD_BLACK);
}
// PRAWA STRONA: Prognoza na dni (4 bloki jeden pod drugim)
display.drawRect(251, 0, 45, 128, GxEPD_BLACK);
for (uint8_t i = 0; i < NUM_DAYS_FORCAST; i++) {
uint16_t y_offset = i * 32;
// DZIEŃ TYGODNIA
uint8_t dayIndex = (i + (DateTime->tm_wday + 1)) % 7;
u8g2.drawStr(294, y_offset + 24, daysOfWeek[dayIndex]);
// IKONA – zawsze dzienna
int idx = IndexOfWeatherID(ForecastID[i]);
display.setFont(&weathericons_regular_webfont10pt7b);
display.setTextColor(GxEPD_BLACK);
display.setCursor(253, y_offset + 32);
display.write(Weathers[idx].IconD);
// LINIA POD BLOKIEM
display.drawFastHLine(251, y_offset, 45, GxEPD_BLACK);
}
u8g2.setFontDirection(0);
}
while (display.nextPage());
}
uint16_t CentreText(const char *TheString) {
// 1. Pobierz fizyczną szerokość tekstu w pikselach dla aktualnej czcionki
uint16_t textWidth = u8g2.getUTF8Width(TheString);
// 2. Oblicz środek (zakładając, że 148 to środek ekranu 296px)
// Jeśli ekran ma inną szerokość: (display.width() / 2) - (textWidth / 2)
uint16_t X = 148 - (textWidth / 2);
// 3. Ustaw kursor i wypisz tekst przez nakładkę u8g2
u8g2.setCursor(X, u8g2.getCursorY());
u8g2.print(TheString);
return X;
}/*
uint16_t CentreText(char *TheString) {
// działa tylko z używaną czcionką
uint16_t X;
X = 148 - float((strlen(TheString) * 11) / 2);//296/2=148
display.setCursor(X, display.getCursorY());
//u8g2.setCursor(X, display.getCursorY());
display.print(TheString);
//u8g2.print(TheString);
return X;
}*/
// dla u8g2_font_unifont_t_polish
uint16_t CentreString(String TheString) {
// 1. Ustawienie czcionki z polskimi znakami
u8g2.setFont(u8g2_font_unifont_t_polish);
// 2. Pobranie precyzyjnej szerokości tekstu w pikselach
uint16_t textWidth = u8g2.getUTF8Width(TheString.c_str());
// 3. Obliczenie X dla środka (używamy szerokości obiektu display)
uint16_t X = (display.width() / 2) - (textWidth / 2);
// 4. Ustawienie kursora (Y pobieramy z u8g2, bo tam go wcześniej ustawiłeś)
u8g2.setCursor(X, u8g2.getCursorY());
// 5. Wypisanie tekstu
u8g2.print(TheString);
return X;
}/*
// dla u8g2_font_helvB12_te
uint16_t CentreString(String TheString) {
// 1. Obliczamy szerokość biorąc pod uwagę polskie znaki (UTF-8)
uint16_t textWidth = u8g2.getUTF8Width(TheString.c_str());
// 2. Dynamicznie wyliczamy środek względem aktualnej szerokości ekranu
// Zamiast sztywnego 148, używamy display.width() / 2
uint16_t X = (display.width() / 2) - (textWidth / 2);
// 3. Ustawiamy kursor i drukujemy przez obiekt u8g2
u8g2.setCursor(X, u8g2.getCursorY());
u8g2.print(TheString);u8g2 automatycznie obsłuży UTF-8
return X;
}
uint16_t CentreString(String TheString) {
// działa tylko z używaną czcionką
uint16_t X;
X = 148 - float((TheString.length() * 11) / 2);
display.setCursor(X, display.getCursorY());
//u8g2.setCursor(X, display.getCursorY());
display.print(TheString);
//u8g2.print(TheString);
return X;
}*/
void DegC() {
// wyświetla oC w bieżącej lokalizacji
display.setCursor(display.getCursorX(), display.getCursorY() - 4);
display.print("°");
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);
}
}
}
/*
// Konfiguracja czcionki pogodowej
//u8g2.setFont(u8g2_font_timR08_tr);
//u8g2.setFont(u8g2_font_t0_14b_te);
//u8g2.setFont(u8g2_font_spleen8x16_me);
//u8g2.setFont(u8g2_font_6x13B_tr);
//u8g2.setFont(u8g2_font_Born2bSportySlab_te);
//u8g2.setFont(u8g2_font_7x13B_mf);
//u8g2.setFont(u8g2_font_Pixellari_te);//ok
//u8g2.setFont(u8g2_font_NokiaSmallBold_te);//niema
//u8g2.setFont(u8g2_font_busdisplay11x5_te);//niema
//u8g2.setFont(u8g2_font_busdisplay8x5_tr);//niema
//u8g2.setFont(u8g2_font_busdisplay11x5_te);//niema
//u8g2.setFont(u8g2_font_commodore64_tr);//niema
//u8g2.setFont(u8g2_font_dystopia_te);//niema
//u8g2.setFont(u8g2_font_frigidaire_mr);//niema
//u8g2.setFont(u8g2_font_sonicmania_te);//niema
//u8g2.setFont(u8g2_font_pixzillav1_te);//niema
//u8g2.setFont(u8g2_font_mildras_te);//niema
//u8g2.setFont(u8g2_font_lastapprenticethin_te);//niema
//u8g2.setFont(u8g2_font_resoledbold_tr);//niema
//u8g2.setFont(u8g2_font_luBS08_te);//lipa
//u8g2.setFont(u8g2_font_prospero_bold_nbp_tr);//bez pl, za duże
//u8g2.setFont(u8g2_font_pxplusibmvga8_mf);//bez pl, za duże
//u8g2.setFont(u8g2_font_sirclive_tr);//szerokie bez pl
//u8g2.setFont(u8g2_font_helvB08_te);//ok cyfry
//u8g2.setFont(u8g2_font_missingplanet_tr);//ok bez pl
//u8g2.setFont(u8g2_font_nerhoe_tr);//ok bez pl
//u8g2.setFont(u8g2_font_rosencrantz_nbp_t_all);//ok bez pl
u8g2.setFont(u8g2_font_unifont_t_weather);
u8g2.setFontMode(1);
u8g2.setFontDirection(0);
int x = 10;
int y = 30;Pamiętaj: Y to linia bazowa (dół znaku)
// Wyświetlamy symbole od kodu 32 (spacja/pierwszy symbol) do 64
// W czcionce weather pod kodami ASCII kryją się ikony
for (int i = 32; i < 64; i++) {
u8g2.setCursor(x, y);
u8g2.print((char)i);
x += 20;Ikony unifont są dość szerokie
if (x > 220) {
x = 10;
y += 35;
}
}*/