• Skip to primary navigation
  • Skip to main content
  • Skip to primary sidebar
  • Skip to secondary sidebar
  • Skip to footer

У Павла!

  • Контроллеры
    • Arduino
      • Приборы
      • Музыка
      • Проекты Arduino
      • Уроки Arduino
      • Игры на Arduino
      • Роботы на Ардуино
      • FLProg
    • Одноплатные ПК
      • Orange pi
      • Raspberry pi
        • Raspberry pi pico
        • Raspberry pi server
        • Проекты Raspberry pi
    • ESP
      • ESP8266
        • NodeMCU
      • ESP32
      • M5stack
    • Другие контроллеры
      • STM32
  • Умный дом
    • Home Assistant
      • Автоматизации
      • Lovelace
    • Tuya
    • Bluetooth
    • ESPHome
    • Frigate
    • Telegram
    • Яндекс
  • ЧПУ
  • 3d печать
  • Об авторе

Бегущая строка показывающая время и погоду на ESP32

14 сентября, 2018

В этой статье рассмотрим совместную работу ESP32 и модуля из 4 матриц 8×8 MAX7219 плюс фоторезистор.

Схема подключения всех компонентов выглядит следующим образом:

Прежде чем приступить к материалу, я Вас попрошу, если нравится то, что я делаю и хотите следить за моей деятельностью, то рекомендую подписаться на мой телеграмм канал: https://t.me/ypavla
Там я публикую новости о вышедших видео, статьях и разные устройства для умного дома и не только показываю.
Спасибо за внимание, теперь продолжим.

 

За основу был взят скетч с этой статьи, но значительно переделан и доработан. Автор данного Скетча Дамир Салахов из г. Самара. Почта “Damir@salakhov.info”

В данном скетче добавил:

  1. Восстановление соединения с Wi-Fi
  2. Нули заменены на букву “O” для удобочитаемости.
  3. Нарисован знак “градуса”.
  4. Добавлена стартовая строка для приветствия.
  5. Кнопка Включения и выключения строки с погодой, чтоб оставить только часы.
  6. Изменение яркости от фоторезистора
  7. %% облачности перевел на русский язык.
  8. Раздвинул цифры на часах для того чтобы не сливались из далека.

С данным человеком я переписывался буквально несколько дней. Но за эти дни я его стал очень сильно уважать. Человек уже в возрасте. Последнюю программу написал 30 лет назад и делает такие вещи, которые в наше время далеко ни каждый увлекающийся этим человек сможет сделать! В том числе и я)

По просьбе Дамира и выкладываю данную программу. Надеюсь кому-то пригодится мне то уж точно!

Ну что-ж приступим к самому скетчу:

/*
Подключения:
esp32    -> Matrix
GPIO23   -> DIN
GPIO18   -> Clk
GPIO5    -> CS
Фоторезистор пин 36. Делитель с пина 3.3в!!!
*/

#include <WiFi.h>
#include <ESP32WebServer.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Max72xxPanel.h>
#include <ArduinoJson.h>

// =======================================================================
// Конфигурация устройства:
// =======================================================================
const char* ssid     = "ds";                               // SSID
const char* password = "";                    // пароль
String APIKEY = "";        // Чтобы получить API ключ, перейдите по ссылке http://openweathermap.org/api
String weatherLang = "&lang=ru";
String cityID = "499099"; //Samara
// =======================================================================

#define BUTTON_PIN  15    // кнопка включения/отключения бегущей строки с погодой
boolean weatherOn = "false";

WiFiClient client;

int photocellPin = 36;     //  фоторезистор
int brightness = 0;
boolean buttonWasUp = true;  // была ли кнопка отпущена  кнопка включения/отключения бегущей строки с погодой


String weatherMain = "";
String weatherDescription = "";
String weatherLocation = "";
String country;
int humidity;
int pressure;
float temp;
float tempMin, tempMax;
int clouds;
float windSpeed;
String date;
String currencyRates;
String weatherString;
int windDeg;
String windDegString;
String cloudsString;
String firstString;

int cntFailedWeather = 0;  // Счетчик отсутстия соединения с сервером
int cntFailedTime = 0;     // Счетчик отсутстия соединения с сервером
boolean start = true;
long period;
int offset=1,refresh=0;
int pinCS = 5;             // Подключение пина CS
int numberOfHorizontalDisplays = 4;   // Количество светодиодных матриц по Горизонтали
int numberOfVerticalDisplays = 1;     // Количество светодиодных матриц по Вертикали
String decodedMsg;
Max72xxPanel matrix = Max72xxPanel(pinCS, numberOfHorizontalDisplays, numberOfVerticalDisplays);
//matrix.cp437(true);

int wait = 20; // скорость бегущей строки

int spacer = 2;
int width = 5 + spacer; // Регулируем расстояние между символами

void setup(void) {

matrix.setIntensity(brightness); // Яркость матрицы от 0 до 15

pinMode(BUTTON_PIN, INPUT_PULLUP);

// начальные координаты матриц 8*8
  matrix.setRotation(0, 1);        // 1 матрица
  matrix.setRotation(1, 1);        // 2 матрица
  matrix.setRotation(2, 1);        // 3 матрица
  matrix.setRotation(3, 1);        // 4 матрица


  Serial.begin(115200);                           // Дебаг
  WiFi.mode(WIFI_STA);                           
  WiFi.begin(ssid, password);                         // Подключаемся к WIFI
  while (WiFi.status() != WL_CONNECTED) {         // Ждем до посинения
    delay(500);
    ScrollText(utf8rus(".")); 
    Serial.print(".");
  }



}

// =======================================================================

int updCnt = 0;
int dots = 0;
long dotTime = 0;
long clkTime = 0;
//int dx=0;
//int dy=0;
byte del=0;
int h,m,s;
// =======================================================================
void loop(void) {



  
  setbrightness();


  if(updCnt<=0) { // каждые 10 циклов получаем данные времени и погоды
    updCnt = 10;
    Serial.println("Getting data ...");
    getWeatherData();
    getTime();
    Serial.println("Data loaded");
    clkTime = millis();
  }

  if(millis()-clkTime > 15000 && !del && dots) { //каждые 15 секунд запускаем бегущую строку
    if (cntFailedWeather != 0 || cntFailedTime != 0) {
      ScrollText(utf8rus("Потеряно соединение с интернетом! Восстанавливается.")); 
      WiFi.begin(ssid, password);                         // Подключаемся к WIFI
      while (WiFi.status() != WL_CONNECTED) {         // Ждем до посинения
        delay(500);
        ScrollText(utf8rus(".")); 
        Serial.print(".");
      }
      cntFailedWeather =0 ;
      cntFailedTime = 0;
    }

  if ( start  ) {                                              // Строка после включенмя один раз
    firstString = ("   Текущие время и погода в " + weatherLocation);
    ScrollText(utf8rus(firstString)); 
    start = ! start;
  }
 
    if (weatherOn)  {                            //  включение/отключение бегущей строки с погодой
      ScrollText(utf8rus(weatherString));         
    }

    updCnt--;
    clkTime = millis();
  }

  DisplayTime();
  if(millis()-dotTime > 500) {
    dotTime = millis();
    dots = !dots;
  }
}

// =======================================================================
void DisplayTime(){
    updateTime();
    matrix.fillScreen(LOW);
    int y = (matrix.height() - 8) / 2; // Центрируем текст по Вертикали

    setbrightness();
    scrollWeatherOn(); 
    
    if(s & 1){matrix.drawChar(14, y-1, (String(":"))[0], HIGH, LOW, 1);} //каждую четную секунду печатаем двоеточие по центру (чтобы мигало)
    else{matrix.drawChar(14, y-1, (String(" "))[0], HIGH, LOW, 1);}

    scrollWeatherOn();     // кнопка включения/отключения бегущей строки с погодой
    
    String hour1 = String (h/10);
    String hour2 = String (h%10);
    String min1 = String (m/10);
    String min2 = String (m%10);
    String sec1 = String (s/10);
    String sec2 = String (s%10);
    int xh = 0;
    int xm = 20;

//    Везде меняем нули на О

    if (hour1[0] == "0"[0]) {
      matrix.drawChar(xh, y, "O"[0], HIGH, LOW, 1);
    } else
    {
      matrix.drawChar(xh, y, hour1[0], HIGH, LOW, 1);
    }

    if (hour2[0] == "0"[0]) {
      matrix.drawChar(xh+7, y, "O"[0], HIGH, LOW, 1);
    } else
    {
      matrix.drawChar(xh+7, y, hour2[0], HIGH, LOW, 1);
    }
    
    if (min1[0] == "0"[0]) {
        matrix.drawChar(xm, y, "O"[0], HIGH, LOW, 1);
    } else
    {
      matrix.drawChar(xm, y, min1[0], HIGH, LOW, 1);
    }

    if (min2[0] == "0"[0]) {
      matrix.drawChar(xm+7, y, "O"[0], HIGH, LOW, 1);
    } else
    {
      matrix.drawChar(xm+7, y, min2[0], HIGH, LOW, 1);
    }




  
    matrix.write(); // Вывод на дисплей
}

// =======================================================================
void DisplayText(String text){
    matrix.fillScreen(LOW);
    for (int i=0; i<text.length(); i++){
    
      int letter =(matrix.width())- i * (width-1);
      int x = (matrix.width() +1) -letter;
      int y = (matrix.height() - 8) / 2; // Центрируем текст по Вертикали
      if (String(text[i]) == "0") {
        matrix.drawChar(x, y, (String("O"))[0], HIGH, LOW, 1);
      } else
      {
        matrix.drawChar(x, y, text[i], HIGH, LOW, 1);
      }
      matrix.write(); // Вывод на дисплей
    
    }

}
// =======================================================================
void ScrollText (String text){
  for ( int i = 0 ; i < width * text.length() + matrix.width() - 1 - spacer; i++ ) {
    if (refresh==1) i=0;
    refresh=0;
    matrix.fillScreen(LOW);
    int letter = i / width;
    int x = (matrix.width() - 1) - i % width;
    int y = (matrix.height() - 8) / 2; // Центрируем текст по Вертикали
 
    while ( x + width - spacer >= 0 && letter >= 0 ) {
      if ( letter < text.length() ) {
        if (String( text[letter]) != "'" ) {  // Рисуем знак градуса вместо апострофа
          
          if (String(text[letter]) == "0") {           // меняем нули на О
            matrix.drawChar(x, y, "O"[0], HIGH, LOW, 1);
          } else
          {
            matrix.drawChar(x, y, text[letter], HIGH, LOW, 1);
          }
          
         
        } else {
          matrix.drawPixel ( x+1,y,HIGH);        // Рисуем знак градуса
          matrix.drawPixel ( x+2,y,HIGH);
          matrix.drawPixel ( x,y+1,HIGH);
          matrix.drawPixel ( x+3,y+1,HIGH);
          matrix.drawPixel ( x,y+2,HIGH);
          matrix.drawPixel ( x+3,y+2,HIGH);
          matrix.drawPixel ( x+1,y+3,HIGH);
          matrix.drawPixel ( x+2,y+3,HIGH);
        }
      }
      letter--;
      x -= width;

          
    }
    matrix.write(); // Вывод на дисплей
    
    setbrightness();
    scrollWeatherOn();        // кнопка включения/отключения бегущей строки с погодой
    
    delay(wait);
  }
}


// =======================================================================
// Берем погоду с сайта openweathermap.org
// =======================================================================



const char *weatherHost = "api.openweathermap.org";



void getWeatherData() //client function to send/receive GET request data.
{
  String result ="";
  WiFiClient client;
  const int httpPort = 80;
  if (!client.connect(weatherHost, httpPort)) {
     Serial.println("connection to openweather failed");
     cntFailedWeather++;   
     return;
    }
    else {
      Serial.println("connection to openweather ok");
      cntFailedWeather = 0;
    }
      // We now create a URI for the request
    String url = "/data/2.5/weather?id="+cityID+"&units=metric&cnt=1&APPID="+APIKEY+ weatherLang;

       // This will send the request to the server
    client.print(String("GET ") + url + " HTTP/1.1\r\n" +
                 "Host: " + weatherHost + "\r\n" +
                 "Connection: close\r\n\r\n");
    unsigned long timeout = millis();
    while (client.available() == 0) {
        if (millis() - timeout > 5000) {
            client.stop();
            return;
        }
    }

    // Read all the lines of the reply from server
    while(client.available()) {
        result = client.readStringUntil('\r');
    }

result.replace('[', ' ');
result.replace(']', ' ');

char jsonArray [result.length()+1];
result.toCharArray(jsonArray,sizeof(jsonArray));
jsonArray[result.length() + 1] = '\0';

StaticJsonBuffer<1024> json_buf;
JsonObject &root = json_buf.parseObject(jsonArray);
if (!root.success())
{
  Serial.println("parseObject() failed");
}

  weatherMain = root["weather"]["main"].as<String>();
  weatherDescription = root["weather"]["description"].as<String>();
  weatherDescription.toLowerCase();
  weatherLocation = root["name"].as<String>();
  country = root["sys"]["country"].as<String>();
  temp = root["main"]["temp"];
  humidity = root["main"]["humidity"];
  pressure = root["main"]["pressure"];

  windSpeed = root["wind"]["speed"];

  clouds = root["main"]["clouds"]["all"];
  String deg = String(char('~'+25));
  weatherString =  "  Температура " + String(temp,1) + "'C ";

  weatherString += " Влажность " + String(humidity) + "% ";
  weatherString += " Давление " + String(int(pressure/1.3332239)) + "ммРтСт ";
  
  

weatherString += " Ветер " + String(windSpeed,1) + "м/с   ";

if (clouds <= 10) cloudsString = "   Ясно";
if (clouds > 10 && clouds <= 30) cloudsString = "   Малооблачно";
if (clouds > 30 && clouds <= 70) cloudsString = "   Средняя облачность";
if (clouds > 70 && clouds <= 95) cloudsString = "   Большая облачность";
if (clouds > 95) cloudsString = "   Пасмурно";

weatherString += cloudsString;

Serial.println(weatherString);
  
}

// =======================================================================
// Берем время у GOOGLE
// =======================================================================

float utcOffset = 4; //поправка часового пояса
long localEpoc = 0;
long localMillisAtUpdate = 0;

void getTime()
{
  WiFiClient client;
  
  if (!client.connect("www.google.com", 80)) {
    Serial.println("connection to google failed");
    cntFailedTime++;
    return;
  }

  Serial.println("connection to google ok"); cntFailedTime = 0;
  client.print(String("GET / HTTP/1.1\r\n") +
               String("Host: www.google.com\r\n") +
               String("Connection: close\r\n\r\n"));
  int repeatCounter = 0;
  while (!client.available() && repeatCounter < 10) {
    delay(500);
    //Serial.println(".");
    repeatCounter++;
  }

  String line;
  client.setNoDelay(false);
  while(client.connected() && client.available()) {
    line = client.readStringUntil('\n');
    line.toUpperCase();
    if (line.startsWith("DATE: ")) {
      date = "     "+line.substring(6, 22);
      h = line.substring(23, 25).toInt();
      m = line.substring(26, 28).toInt();
      s = line.substring(29, 31).toInt();
      localMillisAtUpdate = millis();
      localEpoc = (h * 60 * 60 + m * 60 + s);
    }
  }
  client.stop();
}

// =======================================================================r

void updateTime()
{
  long curEpoch = localEpoc + ((millis() - localMillisAtUpdate) / 1000);
  long epoch = round(curEpoch + 3600 * utcOffset + 86400L) % 86400L;
  h = ((epoch  % 86400L) / 3600) % 24;
  m = (epoch % 3600) / 60;
  s = epoch % 60;
}

// =======================================================================


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 >= 0xC0) {
      switch (n) {
        case 0xD0: {
          n = source[i]; i++;
          if (n == 0x81) { n = 0xA8; break; }
          if (n >= 0x90 && n <= 0xBF) n = n + 0x30-1;
          break;
        }
        case 0xD1: {
          n = source[i]; i++;
          if (n == 0x91) { n = 0xB8; break; }
          if (n >= 0x80 && n <= 0x8F) n = n + 0x70-1;
          break;
        }
      }
    }
    m[0] = n; target = target + String(m);
  }
return target;
}

void setbrightness()
{
    int photocellReading = analogRead(photocellPin);
   
    //      мы должны инвертировать считываемые значения от 0-4095 к 4095-0
    photocellReading = 4095 - photocellReading;
    
    //      теперь мы должны преобразовать диапазон 0-4095 в 0-15 (экспериментально)
    brightness = map(photocellReading, 0, 3500, 2, 15);
    if (brightness > 15 ) {brightness = 15;}
    
    matrix.setIntensity(brightness); // Яркость матрицы от 0 до 15
}   

void scrollWeatherOn()
{
  // сначала понимаем, отпущена ли кнопка прямо сейчас...
  boolean buttonIsUp = digitalRead(BUTTON_PIN);
  // ...если «кнопка была отпущена и (&&) не отпущена сейчас»...
  if (buttonWasUp && !buttonIsUp) {
    // ...может это «клик», а может и ложный сигнал (дребезг),
    // возникающий в момент замыкания/размыкания пластин кнопки,
    // поэтому даём кнопке полностью «успокоиться»...
    delay(10);
    // ...и считываем сигнал снова
    buttonIsUp = digitalRead(BUTTON_PIN);
    if (!buttonIsUp) {  // если она всё ещё нажата...
      // ...это клик! Переворачиваем сигнал светодиода
      weatherOn = !weatherOn;
      Serial.println(weatherOn);
    }
  }
 
  // запоминаем последнее состояние кнопки для новой итерации
  buttonWasUp = buttonIsUp;
}



  

Ссылка на скетч : https://yadi.sk/d/XpUlyTIU790vLw

В принципе тут и добавить нечего. Все функции описаны. Все понятно, что и откуда брать. Единственное для некоторых может быть непонятно где взять (String cityID = “499099”). Это id города которого хотите видеть погоду. Он берется из адресной строки браузера. Например вы ввели на сайте http://openweathermap.org город Mosow и вам открылась погода города Москва. Адрес данной страницы выглядит таким образом: https://openweathermap.org/city/524901 . Где цифры это и есть id нашего города.

Ну думаю на этом все. У кого будут вопросы обязательно пишите Дамиру и он обязательно Вам поможет.

 

P.S. Спасибо еще раз Дамиру Салахову за проделанный труд. Побольше бы таких как Вы. Спасибо.

 

Primary Sidebar

Поиск

Новые записи

  • Умные шторы – как правильно их выбрать?
  • Автоматизация открытия и закрытия штор в Home Assistant.
  • Лучшая карточка Lovelace управления шторами в Home Assistant.
  • Интеграция ИИ Deepseek в Home Assistant
  • Выводим уведомления из умного дома Home Assistant на Android TV с помощью программы TvOverlay.

Официальный YouTube Канал M5Stack:

Подписывайтесь на Телеграм канал

https://t.me/ypavla

Подписаться на YouTube!

Secondary Sidebar




Подписывайтесь на Telegram Канал!

У Павла!

Footer

Copyright_У Павла! © 2025 ·