Часы и погода от Оpenweathermap, ESP32 + дисплей ILI9341

Текущая погода.

  1. Необходимо получить API ключ Оpenweathermap.
    Members
    Производится запрос текущей погоды каждый час.
  2. Настройка библиотеки 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” из архива.
  3. Подключение к WiFi через WiFiManager, IP - 192.168.4.1, сеть = “ESP32-Clock”.
    Настройки в “settings.h”. Есть OTA обновление прошивки.
    Скриншоты, подключение дисплея к ESP32 и фото дисплея в архиве.
    Clock_OpenWeather.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_OpenWeather

Clock_OpenWeather.ino


#include <ArduinoJson.h>          //version - 6.21.2
#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>
#include "fonts.h"
#include "ImgWea60.h"
#include "settings.h"
#define LED_BUILTIN  5        // управление яркостью дисплея 
#define LED_BRIGHTNESS 30     // яркость дисплея при старте
#define ENABLE_OTA_UPDATE true        // OTA обновление
#define RU10 &FreeSansBold10pt8b
#define RU8 &FreeMonoBold8pt8b 
#define RUSW &TinyFont5
#define DIG20 &DIG_Bold_20
#define TFT_W 240 //установить ширину экрана tft
#define TIME_OFFSET timeZone * SECS_PER_HOUR // UTC + timeZone час 
//------------------------------------------------
#define Serial_Print    // отладка для работы  - закоментить
//------------------------------------------------
 WiFiUDP Udp;
 WiFiManager wifiManager;
 TFT_eSPI tft = TFT_eSPI();       
 TFT_eSprite ClockSpr = TFT_eSprite(&tft);
 TFT_eSprite ScrollWeatherSpr = TFT_eSprite(&tft);
 HTTPClient http;
 WiFiClient client;
/*----- Weather Json -----*/
 typedef struct {
   float lon; // 37.71
   float lat; // 51.84
   String description; // "небольшая облачность"
   String icon; // "03n"
   float temp; // 12.56
   float feels_like; // 11.96
   int pressure; // 1022     // приведённое к уровню моря давление.
   int humidity; // 80
   int grnd_level; // 1001   // давление на местности 
   int visibility; // 10000
   float speed; // 2.1
   int deg; // 354
   float gust; // 2.15
   long dt; // 1686079705
   String country; // "RU"
   long sunrise; // 1686013976
   long sunset; // 1686073370
   long id; // 540121
   String name; // "Кшенский"
} weather_t;
 weather_t weather;

 void draw_Clocktime();
 void MSG_Weather_Print(String& MSG_W);
 void draw_Scroll();
 void TFT_weather_cur();
 void draw_Sectime();
 bool decode_json(Stream& jsonStr);
 void Get_Weather_http(String& MSG_http);
 void getWeather();
 void restart();
 void display_brightness();
 void initWiFi();

void print_Img(int x, int y, String WeaIcon) {
  int w, h; w=60; h=60; 
  if      (WeaIcon == "01d") tft.pushImage(x,y,w,h,img_01d);
  else if (WeaIcon == "01n") tft.pushImage(x,y,w,h,img_01n);  
  else if (WeaIcon == "02d") tft.pushImage(x,y,w,h,img_02d);
  else if (WeaIcon == "02n") tft.pushImage(x,y,w,h,img_02n);  
  else if (WeaIcon == "03d" || WeaIcon == "03n") tft.pushImage(x,y,w,h,img_03dn);
  else if (WeaIcon == "04d" || WeaIcon == "04n") tft.pushImage(x,y,w,h,img_04dn);
  else if (WeaIcon == "09d" || WeaIcon == "09n") tft.pushImage(x,y,w,h,img_09dn);
  else if (WeaIcon == "10d" || WeaIcon == "10n") tft.pushImage(x,y,w,h,img_10dn);  
  else if (WeaIcon == "11d" || WeaIcon == "11n") tft.pushImage(x,y,w,h,img_11dn);
  else if (WeaIcon == "13d" || WeaIcon == "13n") tft.pushImage(x,y,w,h,img_13dn);
  else if (WeaIcon == "50d" || WeaIcon == "50n") tft.pushImage(x,y,w,h,img_50dn);
  Serial.println("Icon name: " + WeaIcon);
}// print_Img
/*------------ TFT Clocktime -----------------------------*/
void draw_Clocktime() {
  uint8_t hh = hour(), mm = minute(), ss = second(); 
  ClockSpr.setTextColor(TFT_WHITE, TFT_BLACK);
  ClockSpr.setFreeFont(RU10);
  ClockSpr.drawString(utf8rus( days[ weekday() ] ), 120, ypos + 20);
  ClockSpr.setTextColor(TFT_YELLOW, TFT_BLACK);
  ClockSpr.setFreeFont(DIG20);  
  if (weather.temp > 0)
    ClockSpr.drawString(("+" + String(weather.temp,1) + "@C"), 120,ypos + 50); 
   else                                       //@ - перерисован на знак градуса °C, текущая температура
    ClockSpr.drawString((String(weather.temp,1) + "@C"), 120,ypos + 50);  
  ClockSpr.setFreeFont(RU10);  
  ClockSpr.setTextColor(TFT_GREENYELLOW, TFT_BLACK);
  ClockSpr.drawString( utf8rus( String( day() ) + " " + months[ month() ] + " " + year()), 120, 10 );
  ClockSpr.setTextColor(TFT_WHITE, TFT_BLACK);
  byte omm = 99, oss = 99;
  byte xcolon = 0;
  int x_pos = 51;   // // d:\arduino-1.8.19\...\libraries\TFT_eSPI\examples\320 x 240\TFT_Clock_Digital
  ClockSpr.setTextDatum(TL_DATUM );
  if (omm != mm) {                                         // Обновлять часы и минуты каждую минуту
      omm = mm;                                              // рисуем часы и минуты  
      ClockSpr.setTextColor(TFT_WHITE, TFT_BLACK);
      if (hh < 10) x_pos += ClockSpr.drawChar('0', x_pos, ypos - 40, 7); // Добавить 0 в часы
      x_pos += ClockSpr.drawNumber(hh, x_pos, ypos - 40, 7);             // рисуем часы
      xcolon = x_pos;                                         // Сохранение координат двоеточия, чтобы мигать позже
      x_pos += ClockSpr.drawChar(':', x_pos, ypos - 40, 7);
      if (mm < 10) x_pos += ClockSpr.drawChar('0', x_pos, ypos - 40, 7); // Добавить 0 в минуты 
      x_pos += ClockSpr.drawNumber(mm, x_pos, ypos - 40, 7);             // рисуем минуты
    }
  if (oss != ss) {                                         // Перерисовывать секунды каждую секунду
      oss = ss;
      if (ss % 2) {                                          // Включить/выключить двоеточие
        ClockSpr.setTextColor(TFT_BLACK, TFT_BLACK);        // затемнить двоеточие
        ClockSpr.drawChar(':', xcolon, ypos - 40, 7);                  // Час:минута двоеточие
        ClockSpr.setTextColor(TFT_WHITE, TFT_BLACK);        // Вернуть цвет 
      }
      else {
        ClockSpr.drawChar(':', xcolon, ypos - 40, 7);                  // Час:минута двоеточие
      } 
    ClockSpr.setFreeFont(DIG20);
      x_pos = 103;                                
      if (ss < 10) x_pos += ClockSpr.drawNumber(0, x_pos, ypos - 65); // Добавить 0
      ClockSpr.drawNumber(ss, x_pos, ypos - 65);                     // рисуем секунды
    } 
  ClockSpr.setTextDatum(MC_DATUM);
} // end draw_Clocktime
/*------------ TFT draw_Sectime -----------------------------*/
void draw_Scroll() {
  String MSG_Weather;
  MSG_Weather.reserve(350);
  MSG_Weather += F(" Погода ");
  MSG_Weather += weather.name;
  MSG_Weather += F(",");
  MSG_Weather += weather.description;
  MSG_Weather += F(",Температура ");
  MSG_Weather += String(weather.temp,1);
  MSG_Weather += F("@C,Ощущается как ");
  MSG_Weather += String(weather.feels_like,1);
  MSG_Weather += F("@C,Давление ");
  MSG_Weather += String(weather.grnd_level * 0.750062, 0); // давление на местности
  MSG_Weather += F(" мм,Влажность ");
  MSG_Weather += String(weather.humidity);
  MSG_Weather += F("%,Ветер ");
  MSG_Weather += String(WindDeg_Direction(weather.deg));
  MSG_Weather += F(",");
  MSG_Weather += String(weather.speed, 1);
  MSG_Weather += F(" м/c,Порывы ");
  MSG_Weather += String(weather.gust, 1);
  MSG_Weather += F(" м/c");
  int16_t MSG_Weather_Width;//Ширина  прокручиваемого  всего сообщения в пикселях.
  int Space_Repeats = 50; //Пробел между повторами в пикселях.
  MSG_Weather_Width = tft.textWidth(utf8rus(MSG_Weather))+ Space_Repeats; // -60
  ScrollWeatherSpr.createSprite(MSG_Weather_Width + TFT_W, 12 );
  Step_Count--; 
  if (Step_Count <= 0) { 
     Step_Count = (MSG_Weather_Width / abs(Step_Scroll));
	 MSG_Weather_Print(MSG_Weather);
    }
  ScrollWeatherSpr.scroll(Step_Scroll);    // Сдвиньте спрайт на пиксели Step_Scroll.
  ScrollWeatherSpr.pushSprite(0, 247); // Верхний левый угол спрайта
} // end draw_Scroll

void TFT_weather_cur()  // отрисовка прогноза погоды
{
  String Sun_Time;
  String Sun_Hour;  
  int x_Sun = 80; // координаты отрисовки 
  tft.fillRect ( 0, 259, 239, 60, TFT_BLACK);  // Очистить  Восход - Закат
  print_Img(10, 259, weather.icon);
  tft.setFreeFont(RU10);
  tft.setTextSize(1);  
  tft.setTextColor(TFT_WHITE, TFT_BLACK, true);
  tft.drawString(utf8rus(weather.name), 80, 259);  // нас. пункт
  tft.setFreeFont(RU8);
  tft.setTextSize(1);  
  tft.setTextColor(TFT_GREENYELLOW, TFT_BLACK, true);
  time_t hour_t = hour(weather.sunrise + TIME_OFFSET); // Восход
  time_t minute_t = minute(weather.sunrise + TIME_OFFSET);
  if (hour_t < 10)	// добавить 0 к часам Восход
  Sun_Hour = "0" + String(hour_t);
  else Sun_Hour = String(hour_t); 
  if (minute_t < 10) // добавить 0 к минутам Восход
  Sun_Time = Sun_Hour + ":0" + String(minute_t);
  else Sun_Time = Sun_Hour + ":" + String(minute_t);
  x_Sun +=  tft.drawString(utf8rus("Восход:"), 80, 285);  // Восход
  tft.drawString(Sun_Time, x_Sun+5, 285);  // Восход
  hour_t = hour(weather.sunset + TIME_OFFSET); // Закат
  minute_t = minute(weather.sunset + TIME_OFFSET);  
  if (minute_t < 10)  // добавить 0 к минутам Закат
  Sun_Time = String(hour_t) + ":0" + String(minute_t);
  else Sun_Time = String(hour_t) + ":" + String(minute_t);    
  tft.drawString(utf8rus("Закат:"), 80, 303);   // Закат
  tft.drawString(Sun_Time, x_Sun+5, 303);   // Закат
 } // end TFT_weather_cur
/*------------ TFT draw_Sectime -----------------------------*/
void draw_Sectime() {
  float value, circle = 100;
  double rad = 0.01745;
  String sec_t[12] = {"45", "40", "35", "30", "25", "20", "15", "10", "05", "0", "55", "50"};
  int start[12], startP[60], angle = 0, r = 105, lastAngle = 0, rAngle = 359, b = 0, b2 = 0;
  bool dir = 0;
  for (int i = 0; i < 360; i++) {
    x[i] = (r * cos(rad * i)) + xpos;
    y[i] = (r * sin(rad * i)) + ypos;
    px[i] = ((r - 16) * cos(rad * i)) + xpos;
    py[i] = ((r - 16) * sin(rad * i)) + ypos;
    lx[i] = ((r - 26) * cos(rad * i)) + xpos;
    ly[i] = ((r - 26) * sin(rad * i)) + ypos;
    if (i % 30 == 0) {
      start[b] = i;
      b++;
    }
    if (i % 6 == 0) {
      startP[b2] = i;
      b2++;
    }
  }
  rAngle = rAngle - 2;
  angle = second() * 6;
  if (angle >= 360) angle = 0;
  if (rAngle <= 0) rAngle = 359;
  if (dir == 0) circle = circle + 0.5;
  else  circle = circle - 0.5;
  if (circle > 140) dir = !dir;
  if (circle < 100) dir = !dir;
  if (angle > -1) {
    lastAngle = angle;
    value = ((angle - 270) / 3.60) * -1;
    if (value < 0) value = value + 100;
    ClockSpr.fillSprite(TFT_BLACK);
    ClockSpr.fillCircle(xpos, ypos, 124, TFT_BLACK);
    for (int i = 0; i < 12; i++)
      if (start[i] + angle < 360) {
        ClockSpr.drawString(sec_t[i], x[start[i] + angle], y[start[i] + angle], 2);
        ClockSpr.drawLine(px[start[i] + angle], py[start[i] + angle], lx[start[i] + angle], ly[start[i] + angle], TFT_WHITE);
      }
      else  {
        ClockSpr.drawString(sec_t[i], x[(start[i] + angle) - 360], y[(start[i] + angle) - 360], 2);
        ClockSpr.drawLine(px[(start[i] + angle) - 360], py[(start[i] + angle) - 360], lx[(start[i] + angle) - 360], ly[(start[i] + angle) - 360], TFT_WHITE);
      }
    for (int i = 0; i < 60; i++)
      if (startP[i] + angle < 360)
        ClockSpr.fillCircle(px[startP[i] + angle], py[startP[i] + angle], 1, TFT_YELLOW);
      else
        ClockSpr.fillCircle(px[(startP[i] + angle) - 360], py[(startP[i] + angle) - 360], 1, TFT_YELLOW);
    ClockSpr.fillTriangle(xpos, ypos - 80, xpos - 3, ypos - 70, xpos + 3, ypos - 70, TFT_RED);
   }
}// end draw_Sectime

void setup(void) {
  pinMode(LED_BUILTIN, OUTPUT);
  analogWrite(LED_BUILTIN, LED_BRIGHTNESS); // первоначальная яркость дисплея
  Serial.begin(115200);
  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("Clock_Esp32");
  tft.println(WiFi.SSID());
  tft.print("IP: ");
  tft.println(WiFi.localIP());
  tft.println("Starting UDP");
  Udp.begin(localPort);
  tft.println("Time sync");
  setSyncProvider(getNtpTime);
  setSyncInterval(3600);
  display_brightness();
  delay(2500); // читаем что вывели на дисплей
  if ( ENABLE_OTA_UPDATE )
  {
    tft.println("Begin OTA handler");
    tft.println("Esp32-Clock");
    initOTA(); // обновление по WiFi
  }
  tft.fillScreen(TFT_BLACK);
  ClockSpr.setColorDepth(8);
  ClockSpr.setSwapBytes(true);
  ClockSpr.createSprite(240, 245);
  ClockSpr.setTextDatum(MC_DATUM);
  ScrollWeatherSpr.setTextDatum(TL_DATUM );
  ScrollWeatherSpr.setColorDepth(1); // Глубина цвета сильно влияет на размер спрайта, который вы можете использовать, и на скорость его перемещения.
  ScrollWeatherSpr.setSwapBytes(true);
  ScrollWeatherSpr.setTextSize(2); 
  ScrollWeatherSpr.setFreeFont(RUSW);   
  getWeather();
  TFT_weather_cur();	  
}// end setup

void loop() {
  if ( ENABLE_OTA_UPDATE )
  {
    ArduinoOTA.handle();
  }
  if ((millis() > 60*1000) and (year() == 1970) and (WiFi.status() == WL_CONNECTED))  //при старте время не было задано 
    {
      restart();
     }	
  display_brightness();
  draw_Sectime();
  draw_Clocktime();
  ClockSpr.pushSprite(0, 0);
//  draw_Scroll();
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
     draw_Scroll();
     previousMillis = currentMillis; }  
  if ((millis() - lastTime) > timerDelay*60*1000) {  
     getWeather();
     TFT_weather_cur();	 
     lastTime = millis(); } 
} // end loop

bool decode_json(Stream& jsonStr)
{
  DynamicJsonDocument doc(1024);
  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_OPW = doc.as<JsonObject>();
      weather.lon = doc_OPW["coord"]["lon"].as<float>();
      weather.lat = doc_OPW["coord"]["lat"].as<float>();	
      weather.description = doc_OPW["weather"][0]["description"].as<const char *>();	
      weather.icon = doc_OPW["weather"][0]["icon"].as<const char *>();	
      weather.temp = doc_OPW["main"]["temp"].as<float>();	  
      weather.feels_like = doc_OPW["main"]["feels_like"].as<float>();	  
      weather.pressure = doc_OPW["main"]["pressure"].as<int>();	  
      weather.humidity = doc_OPW["main"]["humidity"].as<int>();	  
      weather.grnd_level = doc_OPW["main"]["grnd_level"].as<int>();	  
      weather.visibility = doc_OPW["visibility"].as<int>();	  
      weather.speed = doc_OPW["wind"]["speed"].as<float>();	  
      weather.deg = doc_OPW["wind"]["deg"].as<int>();	  
      weather.gust = doc_OPW["wind"]["gust"].as<float>();	  
      weather.dt = doc_OPW["dt"].as<long>();	  
      weather.country = doc_OPW["sys"]["country"].as<const char *>();	
      weather.sunrise = doc_OPW["sys"]["sunrise"].as<long>();	  
      weather.sunset = doc_OPW["sys"]["sunset"].as<long>();	  
      weather.id = doc_OPW["id"].as<long>();	  
      weather.name = doc_OPW["name"].as<const char *>();	
     return true;
    }//end else
}//end decode_json

void getWeather() {
  if(WiFi.status()== WL_CONNECTED){   // проверяем соединение WiFi
    String host_uri;
    host_uri.reserve(150);
    host_uri += F("http://api.openweathermap.org/data/2.5/");
    host_uri += F("weather?lat=");
    host_uri += Latitude;
    host_uri += F("&lon=");
    host_uri += Longitude;
    host_uri += F("&lang=ru&appid=");
    host_uri += apikey;
    host_uri += F("&mode=json&units=metric&cnt=1");
    client.stop();
    Get_Weather_http(host_uri);
    int httpCode = http.GET();
    Serial.println(httpCode);
    if (httpCode > 0) { 
      Stream& response = http.getStream(); // ответ  
      decode_json(response); // парсинг данных из JsonObject
#ifdef  Serial_Print  //  отладка      
     Serial.println(" - - weather - - ");
     Serial.print("weather.lon "); Serial.println(weather.lon);
     Serial.print("weather.lat "); Serial.println(weather.lat);
     Serial.print("weather.description "); Serial.println(weather.description);
     Serial.print("weather.icon "); Serial.println(weather.icon);
     Serial.print("weather.temp "); Serial.println(weather.temp);
     Serial.print("weather.feels_like "); Serial.println(weather.feels_like);
     Serial.print("weather.pressure "); Serial.println(weather.pressure);
     Serial.print("weather.humidity "); Serial.println(weather.humidity);
     Serial.print("weather.grnd_level "); Serial.println(weather.grnd_level);
     Serial.print("weather.visibility "); Serial.println(weather.visibility);
     Serial.print("weather.speed "); Serial.println(weather.speed);
     Serial.print("weather.deg "); Serial.println(weather.deg);
     Serial.print("weather.gust "); Serial.println(weather.gust);
     Serial.print("weather.dt "); Serial.println(weather.dt);
     Serial.print("weather.country "); Serial.println(weather.country);
     Serial.print("weather.sunrise "); Serial.println(weather.sunrise);
     Serial.print("weather.sunset "); Serial.println(weather.sunset);
     Serial.print("weather.id "); Serial.println(weather.id);
     Serial.print("weather.name "); Serial.println(weather.name);
#endif
      client.stop();
      http.end();
     }  else
      {
        Serial.println("Connection failed");
        client.stop();
        http.end();
      }
    }
} //end getWeather

String WindDeg_Direction(int Wind_direction) {
  if (Wind_direction >= 338 || Wind_direction < 22)  return Wind_N;  //"Северный";
  if (Wind_direction >=  22 && Wind_direction < 68)  return Wind_NE; //"Северо-Восточный";
  if (Wind_direction >=  68 && Wind_direction < 112) return Wind_E;  //"Восточный";
  if (Wind_direction >= 112 && Wind_direction < 158) return Wind_SE; //"Юго-Восточный";
  if (Wind_direction >= 158 && Wind_direction < 202) return Wind_S;  //"Южный";
  if (Wind_direction >= 202 && Wind_direction < 248) return Wind_SW; //"Юго-Западный";
  if (Wind_direction >= 248 && Wind_direction < 292) return Wind_W;  //"Западный";
  if (Wind_direction >= 292 && Wind_direction < 338) return Wind_NW; //"Северо-Западный";
  return " ?";
}  //end WindDeg_Direction

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

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: ESP32-Clock");
    tft.println("IP: 192.168.4.1");
    wifiManager.autoConnect("ESP32-Clock");
    delay(2000);
  }
} // end initWiFi


void MSG_Weather_Print(String& MSG_W) {
  ScrollWeatherSpr.drawString(utf8rus(MSG_W), TFT_W,0);
}

void Get_Weather_http(String& MSG_http) {
    Serial.println(MSG_http);
    http.begin(client, MSG_http);
}

/*-------------- 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
/*-------- ArduinoOTA code ----------*/
void initOTA()
{
  ArduinoOTA.setHostname("Clock_Esp32");
  Serial.println("Begin OTA handler Clock_Esp32");
  // ArduinoOTA.setPassword("admin");
  ArduinoOTA.onStart([]() {
    String type;
    if (ArduinoOTA.getCommand() == U_FLASH)
      type = "sketch";
    else // U_SPIFFS
      type = "filesystem";
    Serial.println("Start updating " + type);
  });
  ArduinoOTA.onEnd([]() {
    Serial.println("\nEnd");
  });
  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
    Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
  });
  ArduinoOTA.onError([](ota_error_t error) {
    Serial.printf("Error[%u]: ", error);
    if (error == OTA_AUTH_ERROR)         Serial.println("Auth Failed");
    else if (error == OTA_BEGIN_ERROR)   Serial.println("Begin Failed");
    else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
    else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
    else if (error == OTA_END_ERROR)     Serial.println("End Failed");
  });
  ArduinoOTA.begin();
}
/*------- RUS fonts from UTF-8 to Windows-1251 -----*/
String utf8rus(String source)
{
  int i, k;
  String target;
  unsigned char n;
  char m[2] = { '0', '\0' };

  k = source.length(); i = 0;

  while (i < k) {
    n = source[i]; i++;

    if (n >= 127) {
      switch (n) {
        case 208: {
            n = source[i]; i++;
            if (n == 129) {
              n = 192;  // перекодируем букву Ё
              break;
            }
            break;
          }
        case 209: {
            n = source[i]; i++;
            if (n == 145) {
              n = 193;  // перекодируем букву ё
              break;
            }
            break;
          }
      }
    }

    m[0] = n; target = target + String(m);
  }
  return target;
}

//


settings.h


#ifndef SETTINGS_H
#define SETTINGS_H
 unsigned long timerDelay = 60;// минуты -таймер обновления погоды 
// Данные openweathermap.org
 String apikey     = F("ZxZxZxZxZxZxZxZxZxzxzx");  // API key            
 String Latitude   = F("51.8408");                         
 String Longitude  = F("37.7136");                        
// настройки яркости дисплея
 byte slider_day   = 80;              //Яркость день максимум 100
 byte slider_night = 10;            //Яркость ночь
 byte night_       = 22;                  //ночь - время уменьшения яркости дисплея
 byte day_         = 7;                     //день - время увеличения яркости дисплея
// время
 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] = {"", "Воскресенье", "Понедельник", "Вторник", "Среда", "Четверг", "Пятница", "Суббота"}; 
//Ветер
const String Wind_N   = F("Северный");
const String Wind_NE  = F("Северо-Восточный");
const String Wind_E   = F("Восточный");
const String Wind_SE  = F("Юго-Восточный");
const String Wind_S   = F("Южный");
const String Wind_SW  = F("Юго-Западный");
const String Wind_W   = F("Западный");
const String Wind_NW  = F("Северо-Западный");
// Глобальные переменные
 int Step_Count       = 0;        // Счетчик шагов прокрутки
 const long interval  = 320; // интервал прокрутки мсек
 int Step_Scroll      = -25;     // шаг прокрутки
 float x[360], y[360], px[360], py[360], lx[360], ly[360];
 int xpos = 120, ypos = 135;
 unsigned long previousMillis = 0;  
 unsigned long lastTime = 0;                          
#endif

1 лайк

Интересное решение

  • спасибо

чисто эмоционально у меня создаёт впечатление подвисания системы каждую секунду…

В момент прорисовки значений, бегущая строка останавливается

а что часики противосонь, это ничего? )))

Дисплейчик маленький.

Или так.

Что это за шрифт, похожий на семисегментный?

да, с дизайном тут явно не айс

Все файлы тут: https://cloud.mail.ru/public/onpP/Xdm19nV9Q