В этой статье рассмотрим совместную работу ESP32 и модуля из 4 матриц 8×8 MAX7219 плюс фоторезистор.
Схема подключения всех компонентов выглядит следующим образом:
Прежде чем приступить к материалу, я Вас попрошу, если нравится то, что я делаю и хотите следить за моей деятельностью, то рекомендую подписаться на мой телеграмм канал: https://t.me/ypavla
Там я публикую новости о вышедших видео, статьях и разные устройства для умного дома и не только показываю.
Спасибо за внимание, теперь продолжим.
За основу был взят скетч с этой статьи, но значительно переделан и доработан. Автор данного Скетча Дамир Салахов из г. Самара. Почта “Damir@salakhov.info”
В данном скетче добавил:
- Восстановление соединения с Wi-Fi
- Нули заменены на букву “O” для удобочитаемости.
- Нарисован знак “градуса”.
- Добавлена стартовая строка для приветствия.
- Кнопка Включения и выключения строки с погодой, чтоб оставить только часы.
- Изменение яркости от фоторезистора
- %% облачности перевел на русский язык.
- Раздвинул цифры на часах для того чтобы не сливались из далека.
С данным человеком я переписывался буквально несколько дней. Но за эти дни я его стал очень сильно уважать. Человек уже в возрасте. Последнюю программу написал 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. Спасибо еще раз Дамиру Салахову за проделанный труд. Побольше бы таких как Вы. Спасибо.