Текущая погода (иконка, текущая температура, давление, текстовый прогноз)
и прогноз на 4 последующих дня (температура max, температура min, иконка min, прогноз осадков).
Иконки погоды от AccuWeather. AccuWeather APIs | Weather Icons
- Необходимо получить API ключ AccuWeather, тариф “Free”. 50 запросов в сутки.
AccuWeather APIs | User account
Производится запрос текущей погоды каждый час и запрос прогноза погоды на 5 дней каждые 3 часа. - Настройка библиотеки TFT_eSPI (version=2.5.30, оригинал - изменений нет).
GitHub - Bodmer/TFT_eSPI: Arduino and PlatformIO IDE compatible TFT library optimised for the Raspberry Pi Pico (RP2040), STM32, ESP8266 and ESP32 that supports different driver chips
По умолчанию эта библиотека будет работать для ESP8266 с использованием драйвера ILI9341.
Но в нашем случае используем ESP32, поэтому некоторые настройки нужно изменить.
Заменяем файл “User_Setup.h” в корне библиотеки файлом “User_Setup.h” из архива. - В Arduino производим настройки для ESP32 согласно рис. “Arduino настройки.jpg”.
Обязательно выбрать “Partition Sheme → Minimal SPIFFS (1.9MB APP with OTA/190KB SPIFFS )”.
Подключение к WiFi через WiFiManager, IP - 192.168.4.1, сеть = “ESP32AccuClock”.
Настройки в “settings.h”. Есть OTA обновление прошивки.
Скриншоты, подключение дисплея к ESP32 и фото дисплея в архиве.
Clock_esp32_AccuWeather.zip — Яндекс Диск
================================
Подключение дисплея ILI9341 к ESP32 (рис. ili9341_esp32.png)
Display SDO/MISO - подключать не нужно!
Display LED to ESP32 pin 5
Display SCK to ESP32 pin 18
Display SDI/MOSI to ESP32 pin 23
Display DC to ESP32 pin 2
Display RESET to ESP32 pin 4
Display CS to ESP32 pin 15
Display GND to ESP32 pin GND
Display VCC to ESP32 3.3V
===================================
Clock_esp32_AccuWeather.ino
/* ILI9341 with ESP32
// Display SDO/MISO - подключать не нужно!
// Display LED to ESP32 pin 5
// Display SCK to ESP32 pin 18
// Display SDI/MOSI to ESP32 pin 23
// Display DC to ESP32 pin 2
// Display RESET to ESP32 pin 4
// Display CS to ESP32 pin 15
// Display GND to ESP32 pin GND
// Display VCC to ESP32 pin 3.3V
// в библиотеке TFT_eSPI заменить файл User_Setup.h, на прилагаемый
*/
/*--------------- libraries ----------------------*/
#include <ArduinoJson.h> //version - 6.21.1
#include <SPI.h>
#include <TimeLib.h> //version=1.6.1
#include <ArduinoOTA.h>
#include <WiFiUdp.h>
#include <TFT_eSPI.h> // version=2.5.30
#include <WiFiClient.h>
#include <DNSServer.h>
#include <HTTPClient.h>
#include <WiFi.h>
#include <Wire.h>
#include <WiFiManager.h>
/*--------------- Project ----------------------*/
#include "Resources/FreeMonoBold8.h"
#include "Resources/FreeSansBold10.h"
#include "Resources/FreeSansBold14.h"
#include "Resources/DIG15_AR36.h"
#include "Resources/img_1.h"
#include "Resources/img_2.h"
#include "Resources/functions.h"
#include "settings.h" // Настройки - ввести свои данные!!!
#define ENABLE_OTA_UPDATE true // OTA обновление
/*--------------- Fonts ----------------------*/
#define AR36 &ArialRoundedMTBold_36 // шрифт температура сейчас - DIG15_AR36.h
#define RU14 &FreeSansBold14pt8b // шрифт рус. дата - FreeSansBold14.h
#define RU10 &FreeSansBold10pt8b // шрифт рус. температура период - FreeSansBold10.h
#define RU8 &FreeMonoBold8pt8b // шрифт рус. восход, закат - FreeMonoBold8.h
#define DIG15 &DS_DIGI15pt7b // шрифт сек. часов - DIG15_AR36.h
/*---- ILI9341 with ESP32 -----*/
#define TFT_CS 15
#define TFT_DC 2
#define TFT_RST 4
#define TFT_MOSI 23
#define TFT_SCLK 18
#define LED_BUILTIN 5 // управление яркостью дисплея
#define LED_BRIGHTNESS 30 // яркость дисплея при старте
#define TIME_OFFSET timeZone * SECS_PER_HOUR // UTC + timeZone час
//------------------------------------------------
//#define Serial_Print1 // отладка для работы - закомментить
//------------------------------------------------
//------------------------------------------------
//#define Serial_Print2 // отладка для работы - закомментить
//------------------------------------------------
WiFiUDP Udp;
TFT_eSPI tft = TFT_eSPI(); // Использовать аппаратный SPI
WiFiManager wifiManager;
weather_t weather;
CurrentConditions_t Currentweather;
bool decode_json_current(Stream& jsonStr);
bool decode_json_5day(Stream& jsonStr);
void get_Accu_Weather_current();
void get_Accu_Weather_5day();
void print_Img(int x, int y, int Icon_num, String Icon_day);
time_t prevDisplay = 0;
unsigned long lastTime = 0;
/*------------------- TFT weather --------------------------*/
void TFT_weather_current() // отрисовка текущей погоды
{
int Weather_Icon;
get_Accu_Weather_current(); // запрос погоды текущий
Weather_Icon = Currentweather.WeatherIcon;
print_Img(0, 88, Weather_Icon, String("current")); // рисуем иконку текущей погоды
tft.setFreeFont(AR36);
tft.setTextColor(TFT_YELLOW, TFT_BLACK, true);
tft.fillRect ( 100, 88, 139, 34, TFT_BLACK); // Очистить текущая температура
if (Currentweather.Temperature_Value > 0)
tft.drawString(("+" + String(Currentweather.Temperature_Value,1) + "@C"), 100,88);
else //@ - перерисован на знак градуса °C, текущая температура
tft.drawString((String(Currentweather.Temperature_Value,1) + "@C"), 100,88);
tft.setFreeFont(RU10);
tft.setTextColor(TFT_WHITE, TFT_BLACK, true);
tft.fillRect ( 100, 123, 139, 24, TFT_BLACK); // Очистить текущее давление
tft.drawString(String(Currentweather.Pressure_Value* 0.750062,0) + utf8rus(" мм.рт.ст."), 100,123); // текущее давление
tft.setFreeFont(RU8);
tft.setTextColor(TFT_GREENYELLOW, TFT_BLACK, true);
tft.fillRect ( 0, 148, 239, 21, TFT_BLACK); // Очистить текст текущего прогноза
tft.drawString(utf8rus(Currentweather.WeatherText), 1,148);
tft.drawLine( 181, 35, 181, 80, 0x0500 ); // Y-область секунд и дня недели 1
tft.drawLine( 181, 56, 215, 56, 0x0500 ); //X-область секунд и дня недели 2
} // end TFT_weather_current
void TFT_weather_5day() // отрисовка прогноза погоды
{
String str_time;
String str_temp;
String str_liq;
int Weather_Icon;
int xpos; // координаты отрисовки
get_Accu_Weather_5day(); // запрос погоды на 5 дней
tft.setFreeFont(RU10);
tft.setTextColor(TFT_WHITE, TFT_BLACK, true);
str_time = utf8rus( days[ weekday(weather.DailyForecasts[1].EpochDate + TIME_OFFSET) ]);
tft.fillRect ( 0, 170, 239, 72, TFT_BLACK); // Очистить день недели, температура максимум, температура минимум
tft.drawString( str_time, 30 - tft.textWidth(str_time) / 2, 170 ); // день недели
str_time = utf8rus( days[ weekday(weather.DailyForecasts[2].EpochDate + TIME_OFFSET) ]);
tft.drawString( str_time, 90 - tft.textWidth(str_time) / 2, 170 ); // день недели
str_time = utf8rus( days[ weekday(weather.DailyForecasts[3].EpochDate + TIME_OFFSET) ]);
tft.drawString( str_time, 150 - tft.textWidth(str_time) / 2, 170 ); // день недели
str_time = utf8rus( days[ weekday(weather.DailyForecasts[4].EpochDate + TIME_OFFSET) ]);
tft.drawString( str_time, 210 - tft.textWidth(str_time) / 2, 170 ); // день недели
tft.setTextColor(TFT_ORANGE, TFT_BLACK, true);
//'`' - перерисован в шрифте на знак градуса °C
str_temp = String(weather.DailyForecasts[1].Temperature_max_Value,0) +"`C"; // температура максимум
tft.drawString( str_temp, 30 - tft.textWidth(str_temp) / 2, 195 );
str_temp = String(weather.DailyForecasts[2].Temperature_max_Value,0) +"`C";
tft.drawString( str_temp, 90 - tft.textWidth(str_temp) / 2, 195 );
str_temp = String(weather.DailyForecasts[3].Temperature_max_Value,0) +"`C";
tft.drawString( str_temp, 150 - tft.textWidth(str_temp) / 2, 195 );
str_temp = String(weather.DailyForecasts[4].Temperature_max_Value,0) +"`C";
tft.drawString( str_temp, 210 - tft.textWidth(str_temp) / 2, 195 );
tft.setTextColor(0x455F, TFT_BLACK, true); // голубой
str_temp = String(weather.DailyForecasts[1].Temperature_min_Value,0) + "`C"; // температура минимум
tft.drawString( str_temp, 30 - tft.textWidth(str_temp) / 2, 218 );
str_temp = String(weather.DailyForecasts[2].Temperature_min_Value,0) +"`C";
tft.drawString( str_temp, 90 - tft.textWidth(str_temp) / 2, 218 );
str_temp = String(weather.DailyForecasts[3].Temperature_min_Value,0) +"`C";
tft.drawString( str_temp, 150 - tft.textWidth(str_temp) / 2, 218 );
str_temp = String(weather.DailyForecasts[4].Temperature_min_Value,0) +"`C";
tft.drawString( str_temp, 210 - tft.textWidth(str_temp) / 2, 218 );
Weather_Icon = weather.DailyForecasts[1].Day_Icon; // рисуем иконки прогноза погоды
print_Img(0, 243, Weather_Icon, String("mini"));
Weather_Icon = weather.DailyForecasts[2].Day_Icon;
print_Img(60, 243, Weather_Icon, String("mini"));
Weather_Icon = weather.DailyForecasts[3].Day_Icon;
print_Img(120, 243, Weather_Icon, String("mini"));
Weather_Icon = weather.DailyForecasts[4].Day_Icon;
print_Img(180, 243, Weather_Icon, String("mini"));
tft.setFreeFont(RU8);
tft.setTextColor(0x455F, TFT_BLACK, true); // голубой, прогноз осадков
str_liq = String(weather.DailyForecasts[1].Day_TotalLiquid_Value + weather.DailyForecasts[1].Night_TotalLiquid_Value) + "mm";
tft.fillRect ( 0, 285, 239, 17, TFT_BLACK); // Очистить осадки
tft.drawString( str_liq, 30 - tft.textWidth(str_liq) / 2, 285 );
str_liq = String(weather.DailyForecasts[2].Day_TotalLiquid_Value + weather.DailyForecasts[2].Night_TotalLiquid_Value) + "mm";
tft.drawString( str_liq, 90 - tft.textWidth(str_liq) / 2, 285 );
str_liq = String(weather.DailyForecasts[3].Day_TotalLiquid_Value + weather.DailyForecasts[3].Night_TotalLiquid_Value) + "mm";
tft.drawString( str_liq, 150 - tft.textWidth(str_liq) / 2, 285 );
str_liq = String(weather.DailyForecasts[4].Day_TotalLiquid_Value + weather.DailyForecasts[4].Night_TotalLiquid_Value) + "mm";
tft.drawString( str_liq, 210 - tft.textWidth(str_liq) / 2, 285 );
tft.setTextColor(TFT_GREENYELLOW, TFT_BLACK, true);
time_t hour_t = hour(weather.DailyForecasts[0].Sun_EpochRise + TIME_OFFSET); // Восход
time_t minute_t = minute(weather.DailyForecasts[0].Sun_EpochRise + TIME_OFFSET);
if (hour_t < 10) // добавить 0 к часам Восход
str_liq = "0" + String(hour_t);
else str_liq = String(hour_t);
if (minute_t < 10) // добавить 0 к минутам Восход
str_time = str_liq + ":0" + String(minute_t);
else str_time = str_liq + ":" + String(minute_t);
tft.fillRect ( 0, 303, 239, 16, TFT_BLACK); // Очистить Восход - Закат
xpos = tft.drawString(utf8rus("Восход:")+ str_time, 1, 303); // Восход
hour_t = hour(weather.DailyForecasts[0].Sun_EpochSet + TIME_OFFSET); // Закат
minute_t = minute(weather.DailyForecasts[0].Sun_EpochSet + TIME_OFFSET);
if (minute_t < 10) // добавить 0 к минутам Закат
str_time = String(hour_t) + ":0" + String(minute_t);
else str_time = String(hour_t) + ":" + String(minute_t);
tft.drawString(utf8rus("Закат:")+ str_time, xpos+15, 303); // Закат
} // end TFT_weather_5day
/*------------ TFT Clocktime -----------------------------*/
void draw_Clocktime()
{
String str;
int len;
uint8_t hh = hour(), mm = minute(), ss = second(); // Get H, M, S from compile time
if (( hh == 0 ) and ( mm == 0 ) and (ss == 0))
{
tft.setFreeFont(RU14);
tft.setTextColor(TFT_YELLOW, TFT_BLACK, true);
str = utf8rus( days[ weekday() ] );
tft.fillRect ( 182, 57, 57, 30, TFT_BLACK); // Очистить день недели
tft.drawString( str, 186, 57 ); // день недели
str = utf8rus( String( day() ) + " " + months[ month() ] + " " + year() ); // день, месяц и год
tft.fillRect ( 0, 0, 239, 34, TFT_BLACK); // Очистить дату
tft.drawString( str, 120 - tft.textWidth(str) / 2, 2 );
TFT_weather_5day();
}
byte omm = 99, oss = 99;
byte xcolon = 0, xsecs = 0;
int xpos = 35; // // d:\arduino-1.8.19\...\libraries\TFT_eSPI\examples\320 x 240\TFT_Clock_Digital
int ypos = 35; // Верхний левый угол текста часов
int ysecs = ysecs;
if (omm != mm) { // Обновлять часы и минуты каждую минуту
omm = mm; // рисуем часы и минуты
tft.setTextColor(TFT_BLACK, TFT_BLACK, true);
if (hh < 10) xpos += tft.drawChar('8', xpos, ypos, 7); // Добавить 0 в часы, 8 - перерисовывает остатки 2 в 00:00:00
tft.setTextColor(TFT_WHITE, TFT_BLACK, true);
xpos += tft.drawNumber(hh, xpos, ypos, 7); // рисуем часы
xcolon = xpos; // Сохранение координат двоеточия, чтобы мигать позже
xpos += tft.drawChar(':', xpos, ypos, 7);
if (mm < 10) xpos += tft.drawChar('0', xpos, ypos, 7); // Добавить 0 в минуты
xpos += tft.drawNumber(mm, xpos, ypos, 7); // рисуем минуты
xsecs = xpos; // Сохранение положения секунд «x» для последующих обновлений дисплея
}
if (oss != ss) { // Перерисовывать секунды каждую секунду
oss = ss;
xpos = xsecs;
if (ss % 2) { // Включить/выключить двоеточие
tft.setTextColor(TFT_BLACK, TFT_BLACK, true); // затемнить двоеточие
tft.drawChar(':', xcolon, ypos, 7); // Час:минута двоеточие
tft.setTextColor(TFT_WHITE, TFT_BLACK, true); // Вернуть цвет
}
else {
tft.drawChar(':', xcolon, ypos, 7); // Час:минута двоеточие
}
tft.setFreeFont(DIG15);
if (ss < 10) xpos += tft.drawNumber(0, xpos+10, ypos); // Добавить 0
tft.drawNumber(ss, xpos+10, ypos); // рисуем секунды
}
} // end TFT Clocktime
/*------------------ setup -----------------------------------*/
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
analogWrite(LED_BUILTIN, LED_BRIGHTNESS); // первоначальная яркость дисплея
Serial.begin(115200);
delay(250);
tft.begin();
tft.setSwapBytes(true);
tft.setRotation(0);
tft.setTextSize(2);
tft.fillScreen(TFT_BLACK);
tft.setTextColor(TFT_WHITE, TFT_BLACK, true);
tft.setCursor(0, 26);
tft.println("Connecting");
Serial.println("");
Serial.println("NTP Clock");
Serial.print("Connecting to ");
Serial.println(WiFi.SSID());
initWiFi();
tft.println("");
tft.println(WiFi.SSID());
tft.print("IP: ");
tft.println(WiFi.localIP());
tft.println("Starting UDP");
Udp.begin(localPort);
tft.println("Time sync");
setSyncProvider(getNtpTime); //setSyncProvider(getTimeFunction) настраивает время
setSyncInterval(3600); //setSyncInterval(interval) задает количество секунд между синхронизациями
tft.println("Weather request");
display_brightness();
delay(2500); // читаем что вывели на дисплей
if ( ENABLE_OTA_UPDATE )
{
tft.println("Begin OTA handler");
tft.println("Esp32_Clock");
initOTA(); // обновление по WiFi
}
tft.setTextSize(1);
tft.fillScreen(TFT_BLACK);
String str;
tft.setFreeFont(RU14);
tft.setTextColor(TFT_YELLOW, TFT_BLACK, true);
str = utf8rus( days[ weekday() ]);
tft.drawString( str, 186, 55 );
str = utf8rus( String( day() ) + " " + months[ month() ] + " " + year()); // день, месяц и год
tft.drawString( str, 120 - tft.textWidth(str) / 2, 2 );
draw_Clocktime();
TFT_weather_current(); // отрисовка текущей погоды
TFT_weather_5day(); // отрисовка прогноза погоды
}//end setup
/*------------------ loop -----------------------------------*/
void loop()
{
if ( ENABLE_OTA_UPDATE )
{
ArduinoOTA.handle();
}
if (timeStatus() != timeNotSet)
{ // обновляем дисплей только если время поменялось
if (now() != prevDisplay) //Функция now() считывает время, прошедшее с 1 января 1970 года (в секундах).
{
prevDisplay = now();
display_brightness();
refresh_display();
}
}
if ((millis() > 60*1000) and (year() == 1970) and (WiFi.status() == WL_CONNECTED)) //при старте время не было задано
{
restart();
}
if ((millis() - lastTime) > timerDelay*60*60*1000) { // обновление прогноза погоды на 5 дней, timerDelay часы
TFT_weather_5day(); // отрисовка прогноза погоды
lastTime = millis();
}
} //end loop
void refresh_display()
{
draw_Clocktime();
if ( ( minute() == 59 ) && ( second() == 00 ) ) //обновление текущей погоды каждый час в 59 мин 00 секунд
{
TFT_weather_current();
}
} // end refresh_display
void print_Img(int x, int y, int Icon_num, String Icon_day) {
int w, h;
if(Icon_day == "current") {
w=100; //ширина иконки
h=60; //высота иконки
}else{
w=60; //ширина иконки мини
h=36; //высота иконки мини
}
if(Icon_num == 1){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_01); else tft.pushImage(x, y, w, h, img_01m);
}else if(Icon_num == 2){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_02); else tft.pushImage(x, y, w, h, img_02m);
}else if(Icon_num == 3){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_03); else tft.pushImage(x, y, w, h, img_03m);
}else if(Icon_num == 4){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_04); else tft.pushImage(x, y, w, h, img_04m);
}else if(Icon_num == 5){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_05); else tft.pushImage(x, y, w, h, img_05m);
}else if(Icon_num == 6){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_06); else tft.pushImage(x, y, w, h, img_06m);
}else if(Icon_num == 7){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_07); else tft.pushImage(x, y, w, h, img_07m);
}else if(Icon_num == 8){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_08); else tft.pushImage(x, y, w, h, img_08m);
}else if(Icon_num == 11){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_11); else tft.pushImage(x, y, w, h, img_11m);
}else if(Icon_num == 12){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_12); else tft.pushImage(x, y, w, h, img_12m);
}else if(Icon_num == 13){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_13); else tft.pushImage(x, y, w, h, img_13m);
}else if(Icon_num == 14){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_14); else tft.pushImage(x, y, w, h, img_14m);
}else if(Icon_num == 15){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_15); else tft.pushImage(x, y, w, h, img_15m);
}else if(Icon_num == 16){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_16); else tft.pushImage(x, y, w, h, img_16m);
}else if(Icon_num == 17){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_17); else tft.pushImage(x, y, w, h, img_17m);
}else if(Icon_num == 18){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_18); else tft.pushImage(x, y, w, h, img_18m);
}else if(Icon_num == 19){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_19); else tft.pushImage(x, y, w, h, img_19m);
}else if(Icon_num == 20){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_20); else tft.pushImage(x, y, w, h, img_20m);
}else if(Icon_num == 21){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_21); else tft.pushImage(x, y, w, h, img_21m);
}else if(Icon_num == 22){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_22); else tft.pushImage(x, y, w, h, img_22m);
}else if(Icon_num == 23){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_23); else tft.pushImage(x, y, w, h, img_23m);
}else if(Icon_num == 24){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_24); else tft.pushImage(x, y, w, h, img_24m);
}else if(Icon_num == 25){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_25); else tft.pushImage(x, y, w, h, img_25m);
}else if(Icon_num == 26){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_26); else tft.pushImage(x, y, w, h, img_26m);
}else if(Icon_num == 29){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_29); else tft.pushImage(x, y, w, h, img_29m);
}else if(Icon_num == 30){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_30); else tft.pushImage(x, y, w, h, img_30m);
}else if(Icon_num == 31){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_31); else tft.pushImage(x, y, w, h, img_31m);
}else if(Icon_num == 32){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_32); else tft.pushImage(x, y, w, h, img_32m);
}else if(Icon_num == 33){ // последующие мини-иконки для отображения 5 дневного прогноза не используются
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_33); else tft.pushImage(x, y, w, h, img_45m);
}else if(Icon_num == 34){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_34); else tft.pushImage(x, y, w, h, img_45m);
}else if(Icon_num == 35){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_35); else tft.pushImage(x, y, w, h, img_45m);
}else if(Icon_num == 36){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_36); else tft.pushImage(x, y, w, h, img_45m);
}else if(Icon_num == 37){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_37); else tft.pushImage(x, y, w, h, img_45m);
}else if(Icon_num == 38){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_38); else tft.pushImage(x, y, w, h, img_45m);
}else if(Icon_num == 39){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_39); else tft.pushImage(x, y, w, h, img_45m);
}else if(Icon_num == 40){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_40); else tft.pushImage(x, y, w, h, img_45m);
}else if(Icon_num == 41){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_41); else tft.pushImage(x, y, w, h, img_45m);
}else if(Icon_num == 42){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_42); else tft.pushImage(x, y, w, h, img_45m);
}else if(Icon_num == 43){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_43); else tft.pushImage(x, y, w, h, img_45m);
}else if(Icon_num == 44){
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_44); else tft.pushImage(x, y, w, h, img_45m);
}else{
if(Icon_day == "current") tft.pushImage(x, y, w, h, img_45); else tft.pushImage(x, y, w, h, img_45m);
}
} // end print_Img
void restart() // перезапуск ESP32
{
Serial.println("Restart ESP...");
ESP.restart();
} // end restart
void display_brightness() // яркость дисплея
{
if ( ( hour() >= day_ ) and ( hour() < night_ ) )
{
analogWrite(LED_BUILTIN, slider_day);
}
else
{
analogWrite(LED_BUILTIN, slider_night);
}
} // end display_brightness
/*------------------ WiFi -----------------------------------*/
void initWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin();
//WiFi.persistent(false);
WiFi.setAutoReconnect(true);
Serial.println("Connecting to WiFi ..");
while (WiFi.status() != WL_CONNECTED && millis()<15*1000) {
delay(500);
Serial.print(".");
}
if(WiFi.status() == WL_CONNECTED){
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP: ");
Serial.println(WiFi.localIP());
}
else {
Serial.println("WiFi not connected, starting WiFiManager");
tft.println("WiFi not connected..");
tft.println("Starting WiFiManager");
tft.println("SSID: ESP32AccuClock");
tft.print("IP address: ");
tft.println("192.168.4.1");
wifiManager.autoConnect("ESP32AccuClock");
delay(2000);
}
} // end initWiFi
/*------------------ json -----------------------------------*/
bool decode_json_current(Stream& jsonStr)
{
DynamicJsonDocument doc(7000);
DeserializationError error = deserializeJson(doc, jsonStr);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return false;
} else
{
Serial.println("deserializeJson() без ошибок.");
JsonObject root_Accu = doc[0].as<JsonObject>();
Currentweather.WeatherText = root_Accu["WeatherText"].as<const char *>();
Currentweather.WeatherIcon = root_Accu["WeatherIcon"].as<int>();
Currentweather.IsDayTime = root_Accu["IsDayTime"].as<bool>();
Currentweather.Temperature_Value = root_Accu["Temperature"]["Metric"]["Value"].as<float>();
Currentweather.RealFeelTemperature_Value = root_Accu["RealFeelTemperature"]["Metric"]["Value"].as<float>();
Currentweather.RelativeHumidity = root_Accu["RelativeHumidity"].as<int>();
Currentweather.WindSpeed_Value = root_Accu["Wind"]["Speed"]["Metric"]["Value"].as<float>();
Currentweather.WindSpeed_Unit = root_Accu["Wind"]["Speed"]["Metric"]["Unit"].as<const char *>();
Currentweather.Pressure_Value = root_Accu["Pressure"]["Metric"]["Value"].as<float>();
return true;
}//end else
}//end decode_json
bool decode_json_5day(Stream& jsonStr)
{
DynamicJsonDocument doc(28000);
DeserializationError error = deserializeJson(doc, jsonStr);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return false;
} else
{
Serial.println("deserializeJson() без ошибок.");
JsonObject doc_Accu = doc.as<JsonObject>();
weather.Headline.EffectiveEpochDate = doc_Accu["Headline"]["EffectiveEpochDate"].as<long>();;
weather.Headline.Text = doc_Accu["Headline"]["Text"].as<const char *>();
for (uint8_t i = 0; i < 5; i++)
{
weather.DailyForecasts[i].EpochDate = doc_Accu["DailyForecasts"][i]["EpochDate"].as<long>();
weather.DailyForecasts[i].Sun_EpochRise = doc_Accu["DailyForecasts"][i]["Sun"]["EpochRise"].as<long>();
weather.DailyForecasts[i].Sun_EpochSet = doc_Accu["DailyForecasts"][i]["Sun"]["EpochSet"].as<long>();
weather.DailyForecasts[i].Temperature_min_Value = doc_Accu["DailyForecasts"][i]["Temperature"]["Minimum"]["Value"].as<float>();
weather.DailyForecasts[i].Temperature_max_Value = doc_Accu["DailyForecasts"][i]["Temperature"]["Maximum"]["Value"].as<float>();
weather.DailyForecasts[i].Day_Icon = doc_Accu["DailyForecasts"][i]["Day"]["Icon"].as<int>();
weather.DailyForecasts[i].Day_IconPhrase = doc_Accu["DailyForecasts"][i]["Day"]["IconPhrase"].as<const char *>();
weather.DailyForecasts[i].Day_ShortPhrase = doc_Accu["DailyForecasts"][i]["Day"]["ShortPhrase"].as<const char *>();
weather.DailyForecasts[i].Day_Wind_Speed_Value = doc_Accu["DailyForecasts"][i]["Day"]["Wind"]["Speed"]["Value"].as<float>();
weather.DailyForecasts[i].Day_Wind_Speed_Unit = doc_Accu["DailyForecasts"][i]["Day"]["Wind"]["Speed"]["Unit"].as<const char *>();
weather.DailyForecasts[i].Day_TotalLiquid_Value = doc_Accu["DailyForecasts"][i]["Day"]["TotalLiquid"]["Value"].as<int>();
weather.DailyForecasts[i].Night_TotalLiquid_Value = doc_Accu["DailyForecasts"][i]["Night"]["TotalLiquid"]["Value"].as<int>();
}//end for
return true;
}//end else
}//end decode_json
/*-------------- Weather code - current---------------*/
void get_Accu_Weather_current() // запрос погоды
{
if(WiFi.status()== WL_CONNECTED){ // проверяем соединение WiFi
HTTPClient http; // Отправляем HTTP GET запрос
//"http://dataservice.accuweather.com/currentconditions/v1/288916?apikey=1234567890ABCDEF&language=ru-ru&details=true&metric=true"
String _host = "http://dataservice.accuweather.com";
String _uri = "/currentconditions/v1/" + regionID + "?apikey=" + api_key + "&language=" + language + "&details=true&metric=true";
WiFiClient client;
client.stop();
http.begin(client, _host + _uri);
int httpCode = http.GET();
Serial.println(_host + _uri);
Serial.println(httpCode);
if (httpCode > 0) {
Stream& response = http.getStream(); // ответ accuweather
decode_json_current(response); // парсинг данных из JsonObject
#ifdef Serial_Print1 // отладка
Serial.println(" - - CurrentConditions - - ");
Serial.print("Currentweather.WeatherText:= "); Serial.println(Currentweather.WeatherText);
Serial.print("Currentweather.WeatherIcon:= "); Serial.println(Currentweather.WeatherIcon);
Serial.print("Currentweather.IsDayTime:= "); Serial.println(Currentweather.IsDayTime);
Serial.print("Currentweather.Temperature_Value:= "); Serial.println(Currentweather.Temperature_Value);
Serial.print("Currentweather.RealFeelTemperature_Value:= "); Serial.println(Currentweather.RealFeelTemperature_Value);
Serial.print("Currentweather.RelativeHumidity:= "); Serial.println(Currentweather.RelativeHumidity);
Serial.print("Currentweather.WindSpeed_Value:= "); Serial.println(Currentweather.WindSpeed_Value);
Serial.print("Currentweather.WindSpeed_Unit:= "); Serial.println(Currentweather.WindSpeed_Unit);
Serial.print("Currentweather.Pressure_Value:= "); Serial.println(Currentweather.Pressure_Value);
#endif
client.stop();
http.end();
}
else
{
Serial.println("Connection failed");
client.stop();
http.end();
}
}
} // end get_Accu_Weather_current
/*-------------- Weather code - current---------------*/
void get_Accu_Weather_5day() // запрос погоды
{
if(WiFi.status()== WL_CONNECTED){ // проверяем соединение WiFi
HTTPClient http; // Отправляем HTTP GET запрос
//"http://dataservice.accuweather.com/forecasts/v1/daily/5day/288916?apikey=99999999999999999999&language=ru-ru&details=true&metric=true"
String _host = "http://dataservice.accuweather.com";
String _uri = "/forecasts/v1/daily/5day/" + regionID + "?apikey=" + api_key + "&language=" + language + "&details=true&metric=true";
WiFiClient client;
client.stop();
http.begin(client, _host + _uri);
int httpCode = http.GET();
Serial.println(_host + _uri);
Serial.println(httpCode);
if (httpCode > 0) {
Stream& response = http.getStream(); // ответ accuweather
decode_json_5day(response); // парсинг данных из JsonObject
#ifdef Serial_Print2 // отладка
Serial.println(" - - Headline - - ");
Serial.print("Headline.EffectiveEpochDate "); Serial.println(weather.Headline.EffectiveEpochDate);
Serial.print("Headline.Text "); Serial.println(weather.Headline.Text);
for (uint8_t i = 0; i < 5; i++)
{
Serial.print(" - - DailyForecasts: = "); Serial.println(i);
Serial.print("DailyForecasts.EpochDate: = "); Serial.println(weather.DailyForecasts[i].EpochDate);
Serial.print("DailyForecasts.Sun_EpochRise: = "); Serial.println(weather.DailyForecasts[i].Sun_EpochRise);
Serial.print("DailyForecasts.Sun_EpochSet: = "); Serial.println(weather.DailyForecasts[i].Sun_EpochSet);
Serial.print("DailyForecasts.Temperature_min_Value: = "); Serial.println(weather.DailyForecasts[i].Temperature_min_Value);
Serial.print("DailyForecasts.Temperature_max_Value: = "); Serial.println(weather.DailyForecasts[i].Temperature_max_Value);
Serial.print("DailyForecasts.Day_Icon: = "); Serial.println(weather.DailyForecasts[i].Day_Icon);
Serial.print("DailyForecasts.Day_IconPhrase: = "); Serial.println(weather.DailyForecasts[i].Day_IconPhrase);
Serial.print("DailyForecasts.Day_ShortPhrase: = "); Serial.println(weather.DailyForecasts[i].Day_ShortPhrase);
Serial.print("DailyForecasts.Day_Wind_Speed_Value: = "); Serial.println(weather.DailyForecasts[i].Day_Wind_Speed_Value);
Serial.print("DailyForecasts.Day_Wind_Speed_Unit: = "); Serial.println(weather.DailyForecasts[i].Day_Wind_Speed_Unit);
Serial.print("DailyForecasts.Day_TotalLiquid_Value: = "); Serial.println(weather.DailyForecasts[i].Day_TotalLiquid_Value);
Serial.print("DailyForecasts.Night_TotalLiquid_Value: = "); Serial.println(weather.DailyForecasts[i].Night_TotalLiquid_Value);
} //end for
#endif
client.stop();
http.end();
}
else
{
Serial.println("Connection failed");
client.stop();
http.end();
}
}
} // end get_Accu_Weather_5day
/*-------------- NTP code -------------------------*/ // https://wikihandbk.com/wiki/Arduino:%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D1%8B/TimeNTP_ESP8266WiFi
const int NTP_PACKET_SIZE = 48; // NTP-время – в первых 48 байтах сообщения
byte packetBuffer[NTP_PACKET_SIZE]; // буфер для хранения входящих и исходящих пакетов
// отправляем NTP-запрос серверу времени по указанному адресу:
void sendNTPpacket(IPAddress &address)
{
// задаем все байты в буфере на «0»:
memset(packetBuffer, 0, NTP_PACKET_SIZE);
// инициализируем значения для создания NTP-запроса
// (подробнее о пакетах смотрите по ссылке выше)
packetBuffer[0] = 0b11100011; // LI (от «leap indicator», т.е. «индикатор перехода»), версия, режим работы
packetBuffer[1] = 0; // слой (или тип часов)
packetBuffer[2] = 6; // интервал запросов
packetBuffer[3] = 0xEC; // точность
// 8 байтов с нулями, обозначающие базовую задержку и базовую дисперсию:
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49; // После заполнения всех указанных полей
packetBuffer[15] = 52; // вы сможете отправлять пакет с запросом о временной метке
Udp.beginPacket(address, 123); // NTP-запросы к порту 123
Udp.write(packetBuffer, NTP_PACKET_SIZE);
Udp.endPacket();
} // end sendNTPpacket
time_t getNtpTime()
{
IPAddress ntpServerIP; // IP-адрес NTP-сервера
while (Udp.parsePacket() > 0) ; // отбраковываем все пакеты, полученные ранее
Serial.println("Transmit NTP Request"); // "Передача NTP-запроса"
WiFi.hostByName(ntpServerName, ntpServerIP); // подключаемся к случайному серверу из списка
Serial.print(ntpServerName);
Serial.print(": ");
Serial.println(ntpServerIP);
sendNTPpacket(ntpServerIP);
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
Serial.println("Receive NTP Response"); // "Получение NTP-ответа"
Udp.read(packetBuffer, NTP_PACKET_SIZE); // считываем пакет в буфер
unsigned long secsSince1900;
secsSince1900 = (unsigned long)packetBuffer[40] << 24; //конвертируем 4 байта (начиная с позиции 40)
secsSince1900 |= (unsigned long)packetBuffer[41] << 16; //в длинное целое число
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serial.println("No NTP Response :-("); // "Нет NTP-ответа :("
return 0; // если время получить не удалось, возвращаем «0»
} // end getNtpTime
settings.h
#include <Arduino.h>
// обновление текушей погоды происходит каждый час = 24 запроса, чтобы ваш IP не забанил Accuweather не более 50 запросов сутки
int8_t timerDelay = 3;// обновление прогноза погоды на 5 дней, каждые 3 часа + в 00:00:00
// настройки яркости дисплея
byte slider_day = 80; //Яркость день максимум 100
byte slider_night = 10; //Яркость ночь
byte night_ = 22; //ночь - время уменьшения яркости дисплея
byte day_ = 7; //день - время увеличения яркости дисплея
//данные для запроса в Accuweather
String regionID = "288916"; // Код вашего региона в Accuweather
String api_key = "1234567890ABCDEF"; // Key Accuweather
String language = "ru-ru";
// Time
static const char ntpServerName[] = "us.pool.ntp.org";
const int timeZone = 3; // часовой пояс: UTC+3 MSK
unsigned int localPort = 8888; // локальный порт для прослушивания UDP-пакетов
String months[13] = {"", "января", "февраля", "марта", "апреля", "мая", "июня", "июля", "августа", "сентября", "октября", "ноября", "декабря"};
String days[8] = {"", "вс", "пн", "вт", "ср", "чт", "пт", "сб"};